Skip to content
Permalink
Browse files

cargo: start working on unitlibrary loader

The UnitLibrary manages unit lifetimes.

Signed-off-by: Sean Cross <sean@xobs.io>
  • Loading branch information...
xobs committed Oct 4, 2017
1 parent 7bd8c7a commit 8bac63a57d30457f0123a2ebd33e81a570b24ec3
Showing with 220 additions and 111 deletions.
  1. +2 −0 .gitignore
  2. +69 −54 Cargo.lock
  3. +4 −0 src/terminal.rs
  4. +11 −0 src/unitbroadcaster.rs
  5. +103 −27 src/unitlibrary.rs
  6. +6 −1 src/unitloader.rs
  7. +5 −3 src/units/jig.rs
  8. +13 −20 src/units/scenario.rs
  9. +7 −6 src/units/test.rs
@@ -0,0 +1,2 @@
/target/
**/*.rs.bk

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -78,6 +78,8 @@ impl TerminalInterface {
}
self.unit_status.get_mut(stat.kind()).unwrap().insert(stat.name().clone(), stat.status().clone());
},
UnitEvent::RescanStart => (),
UnitEvent::RescanFinish => (),
UnitEvent::Shutdown => (),
}

@@ -92,6 +94,8 @@ impl TerminalInterface {
match event {
UnitEvent::Status(stat) => println!(" {} -> {}", stat.name(), stat.status()),
UnitEvent::Category(stat) => println!("{}: {}", stat.kind(), stat.status()),
UnitEvent::RescanStart => println!("Started unit recsan..."),
UnitEvent::RescanFinish => println!("Finished rescanning units"),
UnitEvent::Shutdown => println!("Shutting down"),
};
}
@@ -217,8 +217,19 @@ impl UnitCategoryEvent {

#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub enum UnitEvent {
/// A unit has updated its status.
Status(UnitStatusEvent),

/// A whole category of units has been updated.
Category(UnitCategoryEvent),

/// A rescan has started.
RescanStart,

/// The rescan has finished.
RescanFinish,

/// The system is shutting down.
Shutdown,
}

@@ -1,7 +1,6 @@
// This UnitLibrary contains all active, loaded modules.
// When

use std::path::PathBuf;
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
@@ -14,13 +13,28 @@ use units::jig::JigDescription;
use units::test::TestDescription;
use units::scenario::ScenarioDescription;


pub struct UnitLibrary {
broadcaster: UnitBroadcaster,
receiver: Receiver<UnitEvent>,
config: Arc<Mutex<Config>>,
jig_descriptions: RefCell<HashMap<UnitName, Arc<Mutex<JigDescription>>>>,
test_descriptions: RefCell<HashMap<UnitName, Arc<Mutex<TestDescription>>>>,
scenario_descriptions: RefCell<HashMap<UnitName, Arc<Mutex<ScenarioDescription>>>>,

/// The unit status is used to determine whether to reload units or not.
unit_status: RefCell<HashMap<UnitName, UnitStatus>>,

/// Currently available jig descriptions. May not be valid.
jig_descriptions: RefCell<HashMap<UnitName, JigDescription>>,

/// Currently available test descriptions. The tests they describe may not be valid.
test_descriptions: RefCell<HashMap<UnitName, TestDescription>>,

/// Currently available scenario descriptions. The scenarios they describe may not be valid.
scenario_descriptions: RefCell<HashMap<UnitName, ScenarioDescription>>,

/// A list of jig names that must be checked when a rescan() is performed.
dirty_jigs: RefCell<HashMap<UnitName, ()>>,
dirty_tests: RefCell<HashMap<UnitName, ()>>,
dirty_scenarios: RefCell<HashMap<UnitName, ()>>,
}

impl UnitLibrary {
@@ -30,33 +44,28 @@ impl UnitLibrary {
broadcaster: broadcaster.clone(),
receiver: broadcaster.subscribe(),
config: config.clone(),
unit_status: RefCell::new(HashMap::new()),
jig_descriptions: RefCell::new(HashMap::new()),
test_descriptions: RefCell::new(HashMap::new()),
scenario_descriptions: RefCell::new(HashMap::new()),
dirty_jigs: RefCell::new(HashMap::new()),
dirty_tests: RefCell::new(HashMap::new()),
dirty_scenarios: RefCell::new(HashMap::new()),
}
}

pub fn update_jig_description(&mut self, jig_description: JigDescription) {
// Notify everyone this unit has been selected.
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_load_started(&jig_description.id())));

self.jig_descriptions.borrow_mut().insert(jig_description.id().clone(), Arc::new(Mutex::new(jig_description)));
/*
// Check to see if the jig is compatible with this platform
if let Err(e) = jig_description.is_compatible(&*self.config.lock().unwrap()) {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_unit_incompatible(name, format!("{}", e))));
return;
}
pub fn update_jig_description(&mut self, description: JigDescription) {

let id = description.id().clone();

// "Select" the Jig, which means we can activate it later on.
let new_jig = match jig_description.select() {
Ok(o) => o,
Err(e) => {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_select_failed(name, format!("{}", e))));
return;
}
};
*/
// Add the jig name to a list of "dirty jigs" that will be checked during "rescan()"
self.dirty_jigs.borrow_mut().insert(id.clone(), ());

// Add an entry to the status to determine whether this unit is new or not.
match self.jig_descriptions.borrow_mut().insert(description.id().clone(), description) {
None => self.unit_status.borrow_mut().insert(id.clone(), UnitStatus::LoadStarted),
Some(_) => self.unit_status.borrow_mut().insert(id.clone(), UnitStatus::UpdateStarted),
};

self.broadcaster
.broadcast(&UnitEvent::Category(UnitCategoryEvent::new(UnitKind::Jig,
@@ -72,7 +81,7 @@ impl UnitLibrary {
// Notify everyone this unit has been selected.
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_load_started(&test_description.id())));

self.test_descriptions.borrow_mut().insert(test_description.id().clone(), Arc::new(Mutex::new(test_description)));
self.test_descriptions.borrow_mut().insert(test_description.id().clone(), test_description);

self.broadcaster
.broadcast(&UnitEvent::Category(UnitCategoryEvent::new(UnitKind::Test,
@@ -88,7 +97,9 @@ impl UnitLibrary {
// Notify everyone this unit has been selected.
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_load_started(&scenario_description.id())));

self.scenario_descriptions.borrow_mut().insert(scenario_description.id().clone(), Arc::new(Mutex::new(scenario_description)));
self.scenario_descriptions
.borrow_mut()
.insert(scenario_description.id().clone(), scenario_description);

self.broadcaster
.broadcast(&UnitEvent::Category(UnitCategoryEvent::new(UnitKind::Scenario,
@@ -100,19 +111,84 @@ impl UnitLibrary {
}

pub fn remove_jig(&mut self, jig_name: &UnitName) {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_unloading(&jig_name)));
self.jig_descriptions.borrow_mut().remove(jig_name);
}

pub fn remove_test(&mut self, test_name: &UnitName) {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_unloading(&test_name)));
self.test_descriptions.borrow_mut().remove(test_name);
}

pub fn remove_scenario(&mut self, scenario_name: &UnitName) {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_unloading(&scenario_name)));
self.scenario_descriptions.borrow_mut().remove(scenario_name);
}

/// Examine all of the loaded units and ensure they can be loaded.
///
/// Start by updating the list of "dirty" items that depend on it:
/// * Jig: Tests and Scenarios
/// * Scenarios: Tests
///
/// Next, delete all "dirty" objects that are Deleted.
///
/// Finally, go through all "dirty" objects and configure them:
/// * Jigs
/// * Tests
/// * Scenarios
/// Where "configure" means check if it's compatible:
/// * If it's compatible and unloaded, then load it
/// * If it's incompatible and loaded, then unload it
/// * If it's compatible and loaded, then do nothing
/// * If it's incompatible and unloaded, then do nothing
pub fn rescan(&mut self) {

self.broadcaster.broadcast(&UnitEvent::RescanStart);

for (jig_name, _) in self.dirty_jigs.borrow().iter() {
for (test_name, test_description) in self.test_descriptions.borrow().iter() {
if test_description.supports_jig(jig_name) {
self.dirty_tests.borrow_mut().insert(test_name.clone(), ());
}
}

for (scenario_name, scenario_description) in self.scenario_descriptions
.borrow()
.iter() {
if scenario_description.supports_jig(jig_name) {
self.dirty_scenarios.borrow_mut().insert(scenario_name.clone(), ());
}
}

// XXX Do something different depending on the Status
self.load_jig(jig_name);
}

self.broadcaster.broadcast(&UnitEvent::RescanFinish);
}

fn load_jig(&self, name: &UnitName) {
assert_eq!(name.kind(), &UnitKind::Jig);

let jig_descriptions = self.jig_descriptions.borrow();

// Get the description.
// It is very much an error if this function is called with an invalid name.
let description = jig_descriptions.get(name).unwrap();

// Check to see if the jig is compatible with this platform
if let Err(e) = description.is_compatible(self, &*self.config.lock().unwrap()) {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_unit_incompatible(name, format!("{}", e))));
return;
}

// "Select" the Jig, which means we can activate it later on.
let new_jig = match description.select() {
Ok(o) => o,
Err(e) => {
self.broadcaster.broadcast(&UnitEvent::Status(UnitStatusEvent::new_select_failed(name, format!("{}", e))));
return;
}
};
}
}
@@ -44,6 +44,8 @@ impl UnitLoader {
match msg {
UnitEvent::Shutdown => return,
UnitEvent::Status(evt) => self.handle_status(&evt),
UnitEvent::RescanStart => (),
UnitEvent::RescanFinish => (),
UnitEvent::Category(_) => (),
}
}
@@ -96,9 +98,12 @@ impl UnitLoader {
}
}
}

// FIXME: Have this call quiesce.
self.library.lock().unwrap().rescan();
}

pub fn unload(&self, name: &UnitName, path: &PathBuf) {
pub fn unload(&self, name: &UnitName, _: &PathBuf) {
match name.kind() {
&UnitKind::Jig => self.library.lock().unwrap().remove_jig(name),
&UnitKind::Test => self.library.lock().unwrap().remove_test(name),
@@ -1,14 +1,16 @@
extern crate systemd_parser;
extern crate runny;

use unit::{UnitName, UnitSelectError, UnitActivateError, UnitDeactivateError, UnitIncompatibleReason, UnitDescriptionError};
use std::path::Path;
use std::io::Read;
use std::fs::File;

use config::Config;
use unit::{UnitName, UnitSelectError, UnitActivateError, UnitDeactivateError, UnitIncompatibleReason, UnitDescriptionError};
use unitlibrary::UnitLibrary;

use self::systemd_parser::items::DirectiveEntry;
use self::runny::Runny;
use config::Config;

pub struct Jig {
name: UnitName,
@@ -92,7 +94,7 @@ impl JigDescription {

/// Determine if a unit is compatible with this system.
/// Returns Ok(()) if it is, and Err(String) if not.
pub fn is_compatible(&self, config: &Config) -> Result<(), UnitIncompatibleReason> {
pub fn is_compatible(&self, _: &UnitLibrary, config: &Config) -> Result<(), UnitIncompatibleReason> {

// If this Jig has a file-existence test, run it.
if let Some(ref test_file) = self.test_file {
@@ -30,13 +30,13 @@ pub struct ScenarioDescription {
description: String,

/// A Vec<String> of jig names that this test is compatible with.
jigs: Vec<String>,
jigs: Vec<UnitName>,

/// A Vec<String> of test names that are explicitly specified.
tests: Vec<String>,
tests: Vec<UnitName>,

/// A Vec<String> of tests that are considered to have passed without running them.
assumptions: Vec<String>,
assumptions: Vec<UnitName>,

/// The maximum duration, if any, for this scenario
timeout: Option<Duration>,
@@ -97,31 +97,19 @@ impl ScenarioDescription {
}
"Jigs" => {
scenario_description.jigs = match directive.value() {
Some(s) => {
s.split(|c| c == ',' || c == ' ')
.map(|s| s.to_string())
.collect()
}
Some(s) => UnitName::from_list(s, "jig")?,
None => vec![],
}
}
"Tests" => {
scenario_description.jigs = match directive.value() {
Some(s) => {
s.split(|c| c == ',' || c == ' ')
.map(|s| s.to_string())
.collect()
}
scenario_description.tests = match directive.value() {
Some(s) => UnitName::from_list(s, "test")?,
None => vec![],
}
}
"Assumptions" => {
scenario_description.jigs = match directive.value() {
Some(s) => {
s.split(|c| c == ',' || c == ' ')
.map(|s| s.to_string())
.collect()
}
scenario_description.assumptions = match directive.value() {
Some(s) => UnitName::from_list(s, "test")?,
None => vec![],
}
}
@@ -138,6 +126,11 @@ impl ScenarioDescription {
&self.id
}

/// Returns true if this scenario is supported on the named jig.
pub fn supports_jig(&self, name: &UnitName) -> bool {
self.jigs.contains(name)
}

/// Determine if a unit is compatible with this system.
/// Returns Ok(()) if it is, and Err(String) if not.
pub fn is_compatible(&self, config: &Config) -> Result<(), UnitIncompatibleReason> {
@@ -36,7 +36,7 @@ pub struct TestDescription {
description: String,

/// A Vec<String> of jig names that this test is compatible with.
jigs: Vec<String>,
jigs: Vec<UnitName>,

/// A Vec<String> of test names that must successfully complete for this test to run.
requires: Vec<String>,
@@ -131,11 +131,7 @@ impl TestDescription {
}
"Jigs" => {
test_description.jigs = match directive.value() {
Some(s) => {
s.split(|c| c == ',' || c == ' ')
.map(|s| s.to_string())
.collect()
}
Some(s) => UnitName::from_list(s, "jig")?,
None => vec![],
}
}
@@ -175,6 +171,11 @@ impl TestDescription {
&self.id
}

/// Returns true if this test is supported on the named jig.
pub fn supports_jig(&self, name: &UnitName) -> bool {
self.jigs.contains(name)
}

/// Determine if a unit is compatible with this system.
/// Returns Ok(()) if it is, and Err(String) if not.
pub fn is_compatible(&self, config: &Config) -> Result<(), UnitIncompatibleReason> {

0 comments on commit 8bac63a

Please sign in to comment.
You can’t perform that action at this time.