1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use std::{collections::HashMap, iter};
use super::{ErrorKind, Result, ResultExt};
use tera::{self, try_get_value, Context, Tera, Value};
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
fn indent(v: Value, m: HashMap<String, Value>) -> tera::Result<Value> {
let s: String = try_get_value!("indent", "value", String, v);
let num_spaces: u64 = m.get("spaces").map(Value::as_u64).unwrap_or(None).unwrap_or(2);
let indent = iter::repeat(' ').take(num_spaces as usize).collect::<String>();
let mut xs = vec![];
for l in s.lines() {
xs.push(if l == "" {
l.to_string()
} else {
format!("{}{}", indent, l)
});
}
Ok(serde_json::to_value(&xs.join("\n")).unwrap())
}
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
fn as_secret(v: Value, _: HashMap<String, Value>) -> tera::Result<Value> {
let s = try_get_value!("secret", "value", String, v);
Ok(format!("SHIPCAT_SECRET::{}", s).into())
}
pub fn render_file_data(data: String, context: &Context) -> Result<String> {
let mut tera = Tera::default();
tera.add_raw_template("one_off", &data)?;
tera.autoescape_on(vec!["html"]);
tera.register_filter("indent", indent);
tera.register_filter("as_secret", as_secret);
let result = tera
.render("one_off", context)
.chain_err(|| ErrorKind::InvalidOneOffTemplate(data))?;
let mut xs = vec![];
for l in result.lines() {
xs.push(l.trim_end());
}
Ok(xs.join("\n"))
}
pub fn one_off(tpl: &str, ctx: &Context) -> Result<String> {
let mut tera = Tera::default();
tera.add_raw_template("one_off", tpl)?;
tera.register_filter("as_secret", as_secret);
let res = tera
.render("one_off", ctx)
.chain_err(|| ErrorKind::InvalidOneOffTemplate(tpl.into()))?;
Ok(res)
}
use super::{Manifest, Region};
impl Manifest {
fn make_template_context(&self, reg: &Region) -> Result<Context> {
let mut ctx = Context::new();
let mut full_env = self.env.plain.clone();
full_env.append(&mut self.secrets.clone());
ctx.insert("env", &full_env);
ctx.insert("service", &self.name.clone());
ctx.insert("environment", ®.environment.to_string());
ctx.insert("region", ®.name.clone());
ctx.insert("kafka", &self.kafka.clone());
ctx.insert("base_urls", ®.base_urls);
ctx.insert("kong", ®.kong);
ctx.insert("cluster", ®.cluster.clone());
ctx.insert("namespace", ®.namespace.clone());
Ok(ctx)
}
pub fn template_configs(&mut self, reg: &Region) -> Result<()> {
let ctx = self.make_template_context(reg)?;
if let Some(ref mut cfg) = self.configs {
for f in &mut cfg.files {
if let Some(ref mut v) = f.value {
let data: String = v.clone();
let svc = self.name.clone();
*v = render_file_data(data, &ctx).chain_err(|| ErrorKind::InvalidTemplate(svc))?;
} else {
bail!("configs must be read first - missing {}", f.name);
}
}
}
Ok(())
}
pub fn template_evars(&mut self, reg: &Region) -> Result<()> {
let ctx = self.make_template_context(reg)?;
for e in &mut self.get_env_vars() {
e.template(&ctx)?;
}
Ok(())
}
}
use super::structs::EnvVars;
impl EnvVars {
pub fn template(&mut self, ctx: &Context) -> Result<()> {
for (_, v) in &mut self.plain.iter_mut() {
*v = one_off(v, &ctx)?;
}
Ok(())
}
}
#[cfg(feature = "filesystem")]
async fn read_arbitrary_template_file(folder: &str, name: &str) -> Result<String> {
use std::path::Path;
use tokio::fs;
let pth = Path::new(".").join(folder).join(format!("{}.j2", name));
if !pth.exists() {
bail!("Template file in {} does not exist", pth.display());
}
let data = fs::read_to_string(&pth).await?;
Ok(data)
}
#[allow(unused_imports)] use super::{Environment, VaultConfig};
impl VaultConfig {
#[cfg(feature = "filesystem")]
pub async fn template(&self, owned_mfs: Vec<String>, env: Environment) -> Result<String> {
let mut ctx = Context::new();
ctx.insert("folder", &self.folder);
ctx.insert("team_owned_services", &owned_mfs);
let tpl = if env == Environment::Prod {
read_arbitrary_template_file("vault", "team-policy-prod.hcl").await?
} else {
read_arbitrary_template_file("vault", "team-policy.hcl").await?
};
let res =
render_file_data(tpl, &ctx).chain_err(|| ErrorKind::InvalidTemplate("vault-template".into()))?;
Ok(res)
}
}