use crate::{errors::*, types::*, util::FutureExt, FirewallBackend};
use bollard::{
container::ListContainersOptions,
models::{ContainerSummary, Network, NetworkContainer},
Docker,
};
use failure::{bail, format_err};
use maplit::hashmap;
use slog::{debug, o, trace, Logger};
use std::collections::HashMap as Map;
pub trait Process<B: FirewallBackend>
where
DFW<B>: Process<B>,
{
fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>>;
}
impl<B, T> Process<B> for Option<T>
where
B: FirewallBackend,
DFW<B>: Process<B>,
T: Process<B>,
{
fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>> {
match self {
Some(t) => t.process(ctx),
None => Ok(None),
}
}
}
impl<B, T> Process<B> for Vec<T>
where
B: FirewallBackend,
DFW<B>: Process<B>,
T: Process<B>,
{
fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>> {
let mut rules = Vec::new();
for rule in self {
if let Some(mut sub_rules) = rule.process(ctx)? {
rules.append(&mut sub_rules);
}
}
Ok(Some(rules))
}
}
pub struct ProcessContext<'a, B>
where
B: FirewallBackend,
DFW<B>: Process<B>,
{
pub(crate) docker: &'a Docker,
pub(crate) dfw: &'a DFW<B>,
pub(crate) container_map: Map<String, ContainerSummary>,
pub(crate) network_map: Map<String, Network>,
pub(crate) external_network_interfaces: Option<Vec<String>>,
pub(crate) primary_external_network_interface: Option<String>,
pub(crate) logger: Logger,
pub(crate) dry_run: bool,
}
impl<'a, B> ProcessContext<'a, B>
where
B: FirewallBackend,
DFW<B>: Process<B>,
{
pub fn new(
docker: &'a Docker,
dfw: &'a DFW<B>,
processing_options: &'a ProcessingOptions,
logger: &'a Logger,
dry_run: bool,
) -> Result<ProcessContext<'a, B>> {
let logger = logger.new(o!());
let list_containers_options = match processing_options.container_filter {
ContainerFilter::All => None,
ContainerFilter::Running => Some(ListContainersOptions {
filters: hashmap! { "status" => vec!["running"]},
..Default::default()
}),
};
let containers = docker.list_containers(list_containers_options).sync()?;
debug!(logger, "Got list of containers";
o!("containers" => format!("{:#?}", containers)));
let container_map = get_container_map(&containers);
trace!(logger, "Got map of containers";
o!("container_map" => format!("{:#?}", container_map)));
let networks = docker.list_networks::<String>(None).sync()?;
debug!(logger, "Got list of networks";
o!("networks" => format!("{:#?}", networks)));
let network_map =
get_network_map(&networks).ok_or_else(|| format_err!("no networks found"))?;
trace!(logger, "Got map of networks";
o!("container_map" => format!("{:#?}", container_map)));
let external_network_interfaces = dfw
.global_defaults
.external_network_interfaces
.as_ref()
.cloned();
let primary_external_network_interface = external_network_interfaces
.as_ref()
.and_then(|v| v.first())
.map(|s| s.to_owned());
Ok(ProcessContext {
docker,
dfw,
container_map,
network_map,
external_network_interfaces,
primary_external_network_interface,
logger,
dry_run,
})
}
pub fn process(&mut self) -> Result<()> {
let rules = Process::<B>::process(self.dfw, self)?;
if let Some(rules) = rules {
B::apply(rules, self)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContainerFilter {
All,
Running,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcessingOptions {
pub container_filter: ContainerFilter,
}
impl Default for ProcessingOptions {
fn default() -> Self {
ProcessingOptions {
container_filter: ContainerFilter::All,
}
}
}
pub(crate) fn get_bridge_name(network_id: &str) -> Result<String> {
if network_id.len() < 12 {
bail!("network has to be longer than 12 characters");
}
Ok(format!("br-{}", &network_id[..12]))
}
pub(crate) fn get_network_for_container(
docker: &Docker,
container_map: &Map<String, ContainerSummary>,
container_name: &str,
network_id: &str,
) -> Result<Option<NetworkContainer>> {
if let Some(container) = container_map.get(container_name) {
Ok(docker
.inspect_network::<String>(network_id, None)
.sync()?
.containers
.and_then(|containers| {
container
.id
.as_ref()
.and_then(|container_id| containers.get(container_id).cloned())
}))
} else {
Ok(None)
}
}
pub(crate) fn get_container_map(containers: &[ContainerSummary]) -> Map<String, ContainerSummary> {
let mut container_map: Map<String, ContainerSummary> = Map::new();
for container in containers {
if let Some(names) = &container.names {
for name in names {
container_map.insert(
name.clone().trim_start_matches('/').to_owned(),
container.clone(),
);
}
}
}
container_map
}
pub(crate) fn get_network_map(networks: &[Network]) -> Option<Map<String, Network>> {
let mut network_map: Map<String, Network> = Map::new();
for network in networks {
if let Some(name) = &network.name {
network_map.insert(name.clone(), network.clone());
}
}
if network_map.is_empty() {
None
} else {
Some(network_map)
}
}
pub(crate) fn generate_marker(components: &[&str]) -> String {
format!("DFW-MARKER:{}", components.join(";"))
}