Relocate the primitive agent into the agent directory and wire in block scoped steps for the dir step

This was WAY EASIER than I thought it would be, which has me extra pumped about
this design decision.

The pipeline.yml sent to the agent:

  ---
  steps:
    - symbol: sh
      parameters:
        script: 'ls -lah | tail -n 5'

    - symbol: sh
      parameters:
        script: 'echo "Hello world from a script"'

    - symbol: dir
      parameters:
        directory: 'stdlib'
        block:
          - symbol: sh
            parameters:
              script: 'pwd && ls -lah'
          - symbol: echo
            parameters:
              message: 'Hello from a block-scoped step!'

    - symbol: unknown
      parameters:
        message: 'this should fail'

Then executing it in the tree:

  ❯ STEPS_DIR=$PWD/tmp ./target/debug/otto-agent ./test-pipeline.yml
  sh exists
  sh exists
  dir exists
  unknown/manifest.yml does not exist, step cannot execute
  NORMALLY THIS WOULD ERROR BEFORE ANYTHING EXECUTES
  ---
  -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 1.5K Oct 22 20:38 test-pipeline.yml
  drwxr-xr-x  5 tyler users 4.0K Oct 22 20:35 tmp
  Hello world from a script
  sh exists
  echo exists
  ---
  /home/tyler/source/git/otto/stdlib
  total 20K
  drwxr-xr-x  5 tyler users 4.0K Oct 22 20:02 .
  drwxr-xr-x 22 tyler users 4.0K Oct 22 20:38 ..
  drwxr-xr-x  4 tyler users 4.0K Oct 22 20:19 dir
  drwxr-xr-x  2 tyler users 4.0K Oct 22 20:28 echo
  drwxr-xr-x  4 tyler users 4.0K Oct 21 19:40 sh
  Hello from a block-scoped step!

Voila!
This commit is contained in:
R Tyler Croy 2020-10-22 20:35:49 -07:00
parent 43116fdd5a
commit c11c05aac0
12 changed files with 82 additions and 101 deletions

23
Cargo.lock generated
View File

@ -215,6 +215,7 @@ dependencies = [
name = "dir-step"
version = "0.1.0"
dependencies = [
"otto-agent 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)",
]
@ -610,6 +611,17 @@ dependencies = [
"tar 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "otto-agent"
version = "0.1.0"
dependencies = [
"nanomsg 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"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]]
name = "otto-auctioneer"
version = "0.1.0"
@ -668,17 +680,6 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "primitive-agent"
version = "0.1.0"
dependencies = [
"nanomsg 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"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]]
name = "proc-macro2"
version = "1.0.18"

View File

@ -3,7 +3,7 @@
members = [
"auctioneer",
"agents/primitive",
"agent",
"eventbus",
"eventbus-cli",

View File

@ -1,12 +1,20 @@
[package]
name = "primitive-agent"
name = "otto-agent"
version = "0.1.0"
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
edition = "2018"
[lib]
name = "ottoagent"
path = "src/lib.rs"
[[bin]]
name = "otto-agent"
path = "src/main.rs"
[dependencies]
serde_yaml = "~0.8.13"
serde = {version = "~1.0.117", features = ["rc", "derive"]}
osp = { path = "../../osp" }
osp = { path = "../osp" }
tempfile = "~3.1.0"
nanomsg = "0.7.2"

View File

@ -1,5 +1,4 @@
use serde::Deserialize;
use serde_yaml::Value;
use std::collections::HashMap;
@ -10,17 +9,17 @@ use std::process::Command;
use tempfile::NamedTempFile;
#[derive(Clone, Debug, Deserialize)]
struct Pipeline {
steps: Vec<Step>,
pub struct Pipeline {
pub steps: Vec<Step>,
}
#[derive(Clone, Debug, Deserialize)]
struct Step {
symbol: String,
parameters: Value,
pub struct Step {
pub symbol: String,
pub parameters: Value,
}
fn run(steps_dir: &str, steps: &Vec<Step>) -> std::io::Result<()> {
pub fn run(steps_dir: &str, steps: &Vec<Step>) -> std::io::Result<()> {
let dir = Path::new(steps_dir);
if ! dir.is_dir() {
@ -55,7 +54,6 @@ fn run(steps_dir: &str, steps: &Vec<Step>) -> std::io::Result<()> {
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();
@ -76,23 +74,7 @@ fn run(steps_dir: &str, steps: &Vec<Step>) -> std::io::Result<()> {
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(())
#[cfg(test)]
mod tests {
}

24
agent/src/main.rs Normal file
View File

@ -0,0 +1,24 @@
use std::fs::File;
use ottoagent::*;
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(())
}

View File

@ -1,4 +0,0 @@
= Otto Agents
This directory contains a number of agent implementations with varying levels
of functionality.

View File

@ -80,6 +80,8 @@ pub enum ParameterType {
StringParameter,
#[serde(rename = "boolean")]
BoolParameter,
#[serde(rename = "block")]
BlockParameter,
}
/** Simple function for serde defaults */

View File

@ -7,3 +7,4 @@ edition = "2018"
[dependencies]
serde_yaml = "~0.8.13"
serde = {version = "~1.0.117", features = ["derive"]}
otto-agent = { path = "../../agent" }

View File

@ -1,64 +1,25 @@
# This manifest captures the basic functionality of the Jenkins Pipeline `sh`
# step
---
# The symbol defines how this step should present in the pipeline
symbol: sh
# Description is help text
symbol: dir
description: |
The `sh` step executes a shell script within the given execution context
The `dir` step executes a collection of steps from within the specified directory
# List all the files/globs to include in the packaged artifact
includes:
# Paths are treated as relative from wherever osp is invoked from
- name: target/release/sh-step
# Steps the entire prefix of the file name, placing the file in the root of
# the artifact
- name: target/release/dir-step
flatten: true
# A name starting with ./ is treated to be relative to the manifest.yml
- name: ./README.adoc
# The entrypoint tells the Otto agent which actual binary to use when
# executing.
entrypoint:
path: sh-step
# Multiarch tells the agent that this should be executed on all platforms. In
# which case case it may be "blindly" invoked.
#
# Non-multiarch steps will be attempt to be invoked with
# `${entrypoint.path}-${arch}-${vendor}-${system}-${abi}` similar to how
# Rust manages target triples: https://doc.rust-lang.org/nightly/rustc/platform-support.html
path: dir-step
multiarch: false
parameters:
- name: script
- name: directory
required: true
type: string
description: |
Runs a Bourne shell script, typically on a Unix node. Multiple lines are accepted.
THe directory to enter into, will error if the directory doesn't already exist
An interpreter selector may be used, for example: `#!/usr/bin/perl`
Otherwise the system default shell will be run, using the `-xe` flags (you can specify `set +e` and/or `set +x` to disable those).
- name: encoding
- name: block
description: |
Encoding of the stdout/stderr output, not typically needed as the system will
default to whatever `LC_TYPE` is defined.
type: string
required: false
- name: label
description: |
A label to identify the shell step in a GUI.
type: string
required: false
- name: returnStatus
description: Compatibility support only, doesn't do anything
type: boolean
required: false
- name: returnStdout
description: Compatibility support only, doesn't do anything
type: boolean
required: false
Block containing the steps to execute in the directory
type: block
required: true

View File

@ -3,9 +3,10 @@
*/
use serde::Deserialize;
use serde_yaml::Value;
use std::fs::File;
use ottoagent::*;
#[derive(Clone, Debug, Deserialize)]
struct Invocation {
parameters: Parameters,
@ -17,12 +18,9 @@ struct Parameters {
block: Vec<Step>,
}
#[derive(Clone, Debug, Deserialize)]
struct Step {
symbol: String,
parameters: Value,
}
fn main() -> std::io::Result<()> {
let steps_dir = std::env::var("STEPS_DIR").expect("STEPS_DIR must be defined");
let args: Vec<String> = std::env::args().collect();
if args.len() != 2 {
@ -37,6 +35,9 @@ fn main() -> std::io::Result<()> {
}
Ok(invoke) => {
// do things
std::env::set_current_dir(&invoke.parameters.directory)
.expect("Failed to set current directory, perhaps it doesn't exist");
run(&steps_dir, &invoke.parameters.block);
Ok(())
}
}

View File

@ -1,4 +1,9 @@
#!/bin/bash
#!/usr/bin/env ruby
require 'yaml'
cat $1 | gawk 'match($0, /\s+message:\s+["](.*)?["]/, a) {print a[1]}'
data = YAML.load(File.read(ARGV.first))
if data['parameters']
puts data['parameters']['message']
end