const APP_NAME: &str = "Docker Container";
const MODULE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
use anyhow::{Context, Result};
use serde_derive::{Deserialize, Serialize};
use super::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Status {
Down,
Unknown,
Up,
Exited,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerContainer {
pub status: Status,
pub instance: AppInstance,
#[serde(skip)]
pub parent: Option<Rc<dyn ContainerTrait>>,
#[serde(skip)]
pub shell: Option<Rc<dyn ContainerTrait>>,
}
impl DockerContainer {
pub fn set_parent(&self, parent: Rc<dyn ContainerTrait>) -> Result<DockerContainer> {
match &self.parent {
Some(x) => log::info!(
"Replacing parent {} on container {} with {}",
x.get_name(),
self.get_name(),
parent.get_name()
),
None => (),
}
Ok(DockerContainer {
parent: Some(parent),
..self.clone()
})
}
pub fn set_shell(&self, preferred: Option<AppQuery>) -> Result<DockerContainer> {
let query = preferred.unwrap_or(AppQuery::new("bash".to_string()));
match &self.shell {
Some(x) => log::info!(
"Replacing shell {} on container {} with {}",
x.get_name(),
self.get_name(),
query.name
),
None => (),
}
let shell = Rc::new(Bash::build(AppInstance::new("bash".to_string()), None)?);
Ok(DockerContainer {
shell: Some(shell),
..self.clone()
})
}
fn get_module_version() -> Result<semver::Version> {
Ok({
semver::Version::parse(MODULE_VERSION).context(format!(
"{} has an invalid version number '{}' Cargo.toml",
APP_NAME, MODULE_VERSION
))
}?)
}
fn get_name(&self) -> String {
match &self.instance.version {
Some(ver) => format!("{} ({})", APP_NAME, ver),
None => format!("{} (Unknown Version)", APP_NAME),
}
}
}
impl AppTrait for DockerContainer {
fn get_name(&self) -> String {
self.get_name()
}
fn build(
instance: AppInstance,
parent: Option<Rc<dyn ContainerTrait>>,
) -> Result<DockerContainer> {
let base = DockerContainer {
status: Status::Down,
instance: AppInstance {
module_version: Some(DockerContainer::get_module_version()?),
..instance.clone()
},
parent,
shell: None,
};
base.set_shell(None)
}
}
impl ContainerTrait for DockerContainer {
fn find(&self, query: AppQuery) -> Result<Vec<AppInstance>> {
let shell = match &self.shell {
None => Err(FoundryError::NotConfigured).context(format!(
"Cannot run find: No shell set in '{}'",
self.get_name()
))?,
Some(x) => x,
};
let parent = match &self.parent {
None => Err(FoundryError::NotConfigured).context(format!(
"Cannot run find: No parent set in '{}'",
self.get_name()
))?,
Some(x) => x,
};
let regex = regex::Regex::new(r"^(\w+) \(([\w\.\s]+)\)$")?;
let lc = shell.get_name().to_lowercase();
let name = regex.captures(&lc).map_or(
Err(FoundryError::UnexpectedValue).context(format!(
"'{}' does not appear to be a valid shell name",
shell.get_name()
)),
|cap| {
cap.get(1).map_or(
Err(FoundryError::UnexpectedValue).context(format!(
"'{}' does not appear to be a valid shell name",
shell.get_name()
)),
|val| Ok(val.as_str()),
)
},
)?;
let cmd = match name {
"bash" => Ok(Message::Command(Cmd {
run_as: None,
command: "bash".to_string(),
args: ["-c", &format!("command -v {}", query.name)]
.iter()
.map(|x| x.to_string())
.collect(),
})),
_ => Err(FoundryError::UnexpectedValue).context(format!(
"Docker containers are not currently set to use '{}' shells",
name
)),
}?;
let location = parent.forward(self.instance.clone(), cmd)?;
Ok(vec![AppInstance::new(query.name.clone())
.set_command_path(Some(Rc::new(self.clone())), location)?])
}
fn cached_apps(&self) -> Result<Vec<AppInstance>> {
unimplemented!("No App Cache for Bash Yet")
}
fn forward(&self, _to: AppInstance, message: Message) -> Result<String> {
match &self.parent {
None => Err(FoundryError::NotConfigured).context(format!(
"Parent isn't set up for forwarding on container {}",
self.instance.name
)),
Some(x) => x.forward(self.instance.clone(), message),
}
}
fn get_name(&self) -> String {
self.get_name()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FindApp(AppQuery);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Action {
Find(FindApp),
Inspect(InspectOptions),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ActionResult {
FindResult(Vec<AppInstance>),
InspectResult,
}
impl Action {
fn _run(&self, _container: DockerContainer) -> Result<ActionResult> {
match self {
Action::Inspect(_) => unimplemented!("Next up, Inspect"),
Action::Find(_query) => unimplemented!(),
}
}
}
impl ActionTrait for FindApp {
type RESPONSE = ActionResult;
fn run(&self, _target: AppInstance) -> Result<Self::RESPONSE> {
unimplemented!()
}
fn to_message(&self, _target: Option<AppInstance>) -> Result<Vec<Message>> {
unimplemented!("ActionTrait not implemented for shell")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InspectOptions {}
impl ActionTrait for InspectOptions {
type RESPONSE = ActionResult;
fn run(&self, _target: AppInstance) -> Result<Self::RESPONSE> {
unimplemented!()
}
fn to_message(&self, _target: Option<AppInstance>) -> Result<Vec<Message>> {
unimplemented!("ActionTrait not implemented for shell")
}
}