1use crate::{errors::*, types::*, util::FutureExt, FirewallBackend};
13use bollard::{
14 container::ListContainersOptions,
15 models::{ContainerSummary, Network, NetworkContainer},
16 Docker,
17};
18use failure::{bail, format_err};
19use maplit::hashmap;
20use slog::{debug, o, trace, Logger};
21use std::collections::HashMap as Map;
22
23pub trait Process<B: FirewallBackend>
63where
64 DFW<B>: Process<B>,
65{
66 fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>>;
71}
72
73impl<B, T> Process<B> for Option<T>
74where
75 B: FirewallBackend,
76 DFW<B>: Process<B>,
77 T: Process<B>,
78{
79 fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>> {
80 match self {
81 Some(t) => t.process(ctx),
82 None => Ok(None),
83 }
84 }
85}
86
87impl<B, T> Process<B> for Vec<T>
88where
89 B: FirewallBackend,
90 DFW<B>: Process<B>,
91 T: Process<B>,
92{
93 fn process(&self, ctx: &ProcessContext<B>) -> Result<Option<Vec<B::Rule>>> {
94 let mut rules = Vec::new();
95 for rule in self {
96 if let Some(mut sub_rules) = rule.process(ctx)? {
97 rules.append(&mut sub_rules);
98 }
99 }
100
101 Ok(Some(rules))
102 }
103}
104
105pub struct ProcessContext<'a, B>
107where
108 B: FirewallBackend,
109 DFW<B>: Process<B>,
110{
111 pub(crate) docker: &'a Docker,
112 pub(crate) dfw: &'a DFW<B>,
113 pub(crate) container_map: Map<String, ContainerSummary>,
114 pub(crate) network_map: Map<String, Network>,
115 pub(crate) external_network_interfaces: Option<Vec<String>>,
116 pub(crate) primary_external_network_interface: Option<String>,
117 pub(crate) logger: Logger,
118 pub(crate) dry_run: bool,
119}
120
121impl<'a, B> ProcessContext<'a, B>
122where
123 B: FirewallBackend,
124 DFW<B>: Process<B>,
125{
126 pub fn new(
128 docker: &'a Docker,
129 dfw: &'a DFW<B>,
130 processing_options: &'a ProcessingOptions,
131 logger: &'a Logger,
132 dry_run: bool,
133 ) -> Result<ProcessContext<'a, B>> {
134 let logger = logger.new(o!());
135
136 let list_containers_options = match processing_options.container_filter {
137 ContainerFilter::All => None,
138 ContainerFilter::Running => Some(ListContainersOptions {
139 filters: hashmap! { "status" => vec!["running"]},
140 ..Default::default()
141 }),
142 };
143 let containers = docker.list_containers(list_containers_options).sync()?;
144 debug!(logger, "Got list of containers";
145 o!("containers" => format!("{:#?}", containers)));
146
147 let container_map = get_container_map(&containers);
148 trace!(logger, "Got map of containers";
149 o!("container_map" => format!("{:#?}", container_map)));
150
151 let networks = docker.list_networks::<String>(None).sync()?;
152 debug!(logger, "Got list of networks";
153 o!("networks" => format!("{:#?}", networks)));
154
155 let network_map =
156 get_network_map(&networks).ok_or_else(|| format_err!("no networks found"))?;
157 trace!(logger, "Got map of networks";
158 o!("container_map" => format!("{:#?}", container_map)));
159
160 let external_network_interfaces = dfw
161 .global_defaults
162 .external_network_interfaces
163 .as_ref()
164 .cloned();
165 let primary_external_network_interface = external_network_interfaces
166 .as_ref()
167 .and_then(|v| v.first())
168 .map(|s| s.to_owned());
169
170 Ok(ProcessContext {
171 docker,
172 dfw,
173 container_map,
174 network_map,
175 external_network_interfaces,
176 primary_external_network_interface,
177 logger,
178 dry_run,
179 })
180 }
181
182 pub fn process(&mut self) -> Result<()> {
184 let rules = Process::<B>::process(self.dfw, self)?;
185 if let Some(rules) = rules {
186 B::apply(rules, self)?;
187 }
188
189 Ok(())
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub enum ContainerFilter {
196 All,
198 Running,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct ProcessingOptions {
205 pub container_filter: ContainerFilter,
208}
209
210impl Default for ProcessingOptions {
211 fn default() -> Self {
212 ProcessingOptions {
213 container_filter: ContainerFilter::All,
214 }
215 }
216}
217
218pub(crate) fn get_bridge_name(network_id: &str) -> Result<String> {
219 if network_id.len() < 12 {
220 bail!("network has to be longer than 12 characters");
221 }
222 Ok(format!("br-{}", &network_id[..12]))
223}
224
225pub(crate) fn get_network_for_container(
226 docker: &Docker,
227 container_map: &Map<String, ContainerSummary>,
228 container_name: &str,
229 network_id: &str,
230) -> Result<Option<NetworkContainer>> {
231 if let Some(container) = container_map.get(container_name) {
232 Ok(docker
233 .inspect_network::<String>(network_id, None)
234 .sync()?
235 .containers
236 .and_then(|containers| {
237 container
238 .id
239 .as_ref()
240 .and_then(|container_id| containers.get(container_id).cloned())
241 }))
242 } else {
243 Ok(None)
244 }
245}
246
247pub(crate) fn get_container_map(containers: &[ContainerSummary]) -> Map<String, ContainerSummary> {
248 let mut container_map: Map<String, ContainerSummary> = Map::new();
249 for container in containers {
250 if let Some(names) = &container.names {
251 for name in names {
252 container_map.insert(
253 name.clone().trim_start_matches('/').to_owned(),
254 container.clone(),
255 );
256 }
257 }
258 }
259
260 container_map
261}
262
263pub(crate) fn get_network_map(networks: &[Network]) -> Option<Map<String, Network>> {
264 let mut network_map: Map<String, Network> = Map::new();
265 for network in networks {
266 if let Some(name) = &network.name {
267 network_map.insert(name.clone(), network.clone());
268 }
269 }
270
271 if network_map.is_empty() {
272 None
273 } else {
274 Some(network_map)
275 }
276}
277
278pub(crate) fn generate_marker(components: &[&str]) -> String {
279 format!("DFW-MARKER:{}", components.join(";"))
280}