docker_wrapper/template/web/
nginx.rs1#![allow(clippy::doc_markdown)]
4#![allow(clippy::must_use_candidate)]
5#![allow(clippy::return_self_not_must_use)]
6
7use crate::template::{HealthCheck, Template, TemplateConfig, VolumeMount};
8use async_trait::async_trait;
9use std::collections::HashMap;
10
11pub struct NginxTemplate {
13 config: TemplateConfig,
14}
15
16impl NginxTemplate {
17 pub fn new(name: impl Into<String>) -> Self {
19 let name = name.into();
20 let env = HashMap::new();
21
22 let config = TemplateConfig {
23 name: name.clone(),
24 image: "nginx".to_string(),
25 tag: "alpine".to_string(),
26 ports: vec![(80, 80)],
27 env,
28 volumes: Vec::new(),
29 network: None,
30 health_check: Some(HealthCheck {
31 test: vec![
32 "wget".to_string(),
33 "--no-verbose".to_string(),
34 "--tries=1".to_string(),
35 "--spider".to_string(),
36 "http://localhost/".to_string(),
37 ],
38 interval: "30s".to_string(),
39 timeout: "10s".to_string(),
40 retries: 3,
41 start_period: "10s".to_string(),
42 }),
43 auto_remove: false,
44 memory_limit: None,
45 cpu_limit: None,
46 platform: None,
47 };
48
49 Self { config }
50 }
51
52 pub fn port(mut self, port: u16) -> Self {
54 if let Some(pos) = self.config.ports.iter().position(|(_, c)| *c == 80) {
56 self.config.ports[pos] = (port, 80);
57 } else {
58 self.config.ports.push((port, 80));
59 }
60 self
61 }
62
63 pub fn https_port(mut self, port: u16) -> Self {
65 self.config.ports.push((port, 443));
67 self
68 }
69
70 pub fn content(mut self, content_path: impl Into<String>) -> Self {
72 self.config.volumes.push(VolumeMount {
73 source: content_path.into(),
74 target: "/usr/share/nginx/html".to_string(),
75 read_only: true,
76 });
77 self
78 }
79
80 pub fn config_file(mut self, config_path: impl Into<String>) -> Self {
82 self.config.volumes.push(VolumeMount {
83 source: config_path.into(),
84 target: "/etc/nginx/nginx.conf".to_string(),
85 read_only: true,
86 });
87 self
88 }
89
90 pub fn sites_config(mut self, sites_path: impl Into<String>) -> Self {
92 self.config.volumes.push(VolumeMount {
93 source: sites_path.into(),
94 target: "/etc/nginx/conf.d".to_string(),
95 read_only: true,
96 });
97 self
98 }
99
100 pub fn ssl_certs(mut self, certs_path: impl Into<String>) -> Self {
102 self.config.volumes.push(VolumeMount {
103 source: certs_path.into(),
104 target: "/etc/nginx/ssl".to_string(),
105 read_only: true,
106 });
107 self
108 }
109
110 pub fn logs(mut self, logs_path: impl Into<String>) -> Self {
112 self.config.volumes.push(VolumeMount {
113 source: logs_path.into(),
114 target: "/var/log/nginx".to_string(),
115 read_only: false,
116 });
117 self
118 }
119
120 pub fn memory_limit(mut self, limit: impl Into<String>) -> Self {
122 self.config.memory_limit = Some(limit.into());
123 self
124 }
125
126 pub fn worker_processes(mut self, count: impl Into<String>) -> Self {
128 self.config
129 .env
130 .insert("NGINX_WORKER_PROCESSES".to_string(), count.into());
131 self
132 }
133
134 pub fn worker_connections(mut self, count: impl Into<String>) -> Self {
136 self.config
137 .env
138 .insert("NGINX_WORKER_CONNECTIONS".to_string(), count.into());
139 self
140 }
141
142 pub fn version(mut self, version: impl Into<String>) -> Self {
144 self.config.tag = format!("{}-alpine", version.into());
145 self
146 }
147
148 pub fn network(mut self, network: impl Into<String>) -> Self {
150 self.config.network = Some(network.into());
151 self
152 }
153
154 pub fn auto_remove(mut self) -> Self {
156 self.config.auto_remove = true;
157 self
158 }
159
160 pub fn as_reverse_proxy(mut self) -> Self {
162 if let Some(health) = &mut self.config.health_check {
164 health.test = vec!["nginx".to_string(), "-t".to_string()];
165 }
166 self
167 }
168
169 pub fn debug(mut self) -> Self {
171 self.config
172 .env
173 .insert("NGINX_DEBUG".to_string(), "true".to_string());
174 self
175 }
176
177 pub fn custom_image(mut self, image: impl Into<String>, tag: impl Into<String>) -> Self {
179 self.config.image = image.into();
180 self.config.tag = tag.into();
181 self
182 }
183
184 pub fn platform(mut self, platform: impl Into<String>) -> Self {
186 self.config.platform = Some(platform.into());
187 self
188 }
189}
190
191#[async_trait]
192impl Template for NginxTemplate {
193 fn name(&self) -> &str {
194 &self.config.name
195 }
196
197 fn config(&self) -> &TemplateConfig {
198 &self.config
199 }
200
201 fn config_mut(&mut self) -> &mut TemplateConfig {
202 &mut self.config
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_nginx_template_basic() {
212 let template = NginxTemplate::new("test-nginx");
213 assert_eq!(template.name(), "test-nginx");
214 assert_eq!(template.config().image, "nginx");
215 assert_eq!(template.config().tag, "alpine");
216 assert_eq!(template.config().ports, vec![(80, 80)]);
217 }
218
219 #[test]
220 fn test_nginx_template_with_ports() {
221 let template = NginxTemplate::new("test-nginx").port(8080).https_port(8443);
222
223 assert_eq!(template.config().ports.len(), 2);
224 assert!(template.config().ports.contains(&(8080, 80)));
225 assert!(template.config().ports.contains(&(8443, 443)));
226 }
227
228 #[test]
229 fn test_nginx_template_with_content() {
230 let template = NginxTemplate::new("test-nginx")
231 .content("./website")
232 .config_file("./nginx.conf");
233
234 assert_eq!(template.config().volumes.len(), 2);
235 assert_eq!(template.config().volumes[0].source, "./website");
236 assert_eq!(template.config().volumes[0].target, "/usr/share/nginx/html");
237 assert!(template.config().volumes[0].read_only);
238
239 assert_eq!(template.config().volumes[1].source, "./nginx.conf");
240 assert_eq!(template.config().volumes[1].target, "/etc/nginx/nginx.conf");
241 assert!(template.config().volumes[1].read_only);
242 }
243
244 #[test]
245 fn test_nginx_template_reverse_proxy() {
246 let template = NginxTemplate::new("test-nginx").as_reverse_proxy();
247
248 if let Some(health) = &template.config().health_check {
249 assert_eq!(health.test, vec!["nginx".to_string(), "-t".to_string()]);
250 }
251 }
252
253 #[test]
254 fn test_nginx_template_with_ssl() {
255 let template = NginxTemplate::new("test-nginx")
256 .port(80)
257 .https_port(443)
258 .ssl_certs("./certs");
259
260 assert!(template.config().ports.contains(&(80, 80)));
261 assert!(template.config().ports.contains(&(443, 443)));
262
263 let ssl_volume = template
264 .config()
265 .volumes
266 .iter()
267 .find(|v| v.target == "/etc/nginx/ssl")
268 .expect("SSL volume should be present");
269 assert_eq!(ssl_volume.source, "./certs");
270 }
271}