use std::thread;
use std::time;
use crate::args;
use crate::cmd::{CommandCompose, CommandRun};
use crate::command_runner::CommandRunner;
#[cfg(test)]
use crate::command_runner::TestCommandRunner;
use crate::errors::*;
use crate::pod::{Pod, PodType};
use crate::project::{PodOrService, Project};
use crate::runtime_state::RuntimeState;
pub trait CommandUp {
fn up<CR>(
&self,
runner: &CR,
act_on: &args::ActOn,
opts: &args::opts::Up,
) -> Result<()>
where
CR: CommandRunner;
fn init_pod<CR>(&self, runner: &CR, pod: &Pod) -> Result<()>
where
CR: CommandRunner;
}
impl CommandUp for Project {
fn up<CR>(
&self,
runner: &CR,
act_on: &args::ActOn,
opts: &args::opts::Up,
) -> Result<()>
where
CR: CommandRunner,
{
let pods_or_services = act_on
.pods_or_services(self)
.filter(|v| match *v {
Ok(ref p_s) => p_s.pod_type() != PodType::Task,
Err(_) => true,
});
for pod_or_service in pods_or_services {
match pod_or_service? {
PodOrService::Pod(pod) => {
self.compose_pod(runner, "up", pod, opts)?;
if opts.init {
self.init_pod(runner, pod)?;
}
}
PodOrService::Service(pod, service_name) => {
self.compose_service(runner, "up", pod, service_name, opts)?;
}
}
}
Ok(())
}
fn init_pod<CR>(&self, runner: &CR, pod: &Pod) -> Result<()>
where
CR: CommandRunner,
{
if pod.run_on_init().is_empty() {
return Ok(());
}
println!(
"Waiting for pod '{}' to be listening on all ports",
pod.name()
);
loop {
let state: RuntimeState = RuntimeState::for_project(self)?;
let listening = pod
.service_names()
.iter()
.map(|service_name| {
debug!("scanning service '{}'", service_name);
let containers = state.service_containers(service_name);
if containers.is_empty() {
debug!("no containers for service '{}' yet", service_name);
false
} else {
containers
.iter()
.map(|container| container.is_listening_to_ports())
.all(|listening| listening)
}
})
.all(|listening| listening);
if listening {
break;
}
thread::sleep(time::Duration::from_millis(250));
}
println!("Initializing pod '{}'", pod.name());
for cmd in pod.run_on_init() {
if cmd.is_empty() {
return Err("all `run_on_init` items for '{}' \
must have at least one value"
.into());
}
let service = &cmd[0];
let cmd = if cmd.len() >= 2 {
Some(args::Command::new(&cmd[1]).with_args(&cmd[2..]))
} else {
None
};
let opts = args::opts::Run::default();
self.run(runner, service, cmd.as_ref(), &opts)?;
}
Ok(())
}
}
#[test]
fn runs_docker_compose_up_honors_enable_in_targets() {
let _ = env_logger::try_init();
let mut proj = Project::from_example("rails_hello").unwrap();
proj.set_current_target_name("production").unwrap();
let runner = TestCommandRunner::new();
proj.output("up").unwrap();
let opts = args::opts::Up::default();
proj.up(&runner, &args::ActOn::All, &opts).unwrap();
assert_ran!(runner, {
[
"docker-compose",
"-p",
"railshello",
"-f",
proj.output_dir().join("pods").join("frontend.yml"),
"up",
"-d",
]
});
proj.remove_test_output().unwrap();
}