Skip to content

Commit

Permalink
Clean up parameter file code.
Browse files Browse the repository at this point in the history
  • Loading branch information
jimmycuadra committed Mar 21, 2017
1 parent c43cdea commit 30c8210
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 182 deletions.
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ FLAGS:
OPTIONS:
-b, --base64-parameter <NAME> <VALUE>
Same as --parameter, but for values already encoded in Base64
-f, --param-file <FILENAME>...
Supplies a Yaml file defining any named parameters
-p, --parameter <NAME> <VALUE>
Supplies a value for the named parameter
-f, --parameter-file <PARAMETER_FILE>...
Path to a YAML file with parameter values
-s, --secret <NAME> <NAMESPACE>
A secret to Base64 encode after parameter interpolation
Expand All @@ -35,10 +35,11 @@ ARGS:
Run `ktmpl TEMPLATE` where TEMPLATE is a path to a Kubernetes manifest template in YAML format.
The included [example.yml](example.yml) is a working example template.

To provide values for template parameters, use either the `--parameter` or the `--parameter-file` option to supply key-value pairs.
Using the `--parameter` option:
To provide values for template parameters, use the `--parameter`, `--base64-parameter` or `--parameter-file` options.

The included `example.yml` and `params.yml` files are used in the following examples.

Using the provided example.yml, this would mean:
Using the `--parameter` option:

``` bash
ktmpl example.yml --parameter MONGODB_PASSWORD secret
Expand All @@ -47,12 +48,19 @@ ktmpl example.yml --parameter MONGODB_PASSWORD secret
Using the `--parameter-file` option:

```
ktmpl example.yml --parameter-file params.yaml
ktmpl example.yml --parameter-file params.yml
```

If the same parameter is defined more than once, the last defined value will be used. Passing parameters on the command line `--parameter` will override any that are defined via the `--parameter-file` construct
A parameter file must contain one or more YAML documents.
Each document must be a YAML hash with string keys and string values, corresponding to template
parameters and their values, respectively.

In parameter files, if the same parameter is defined more than once, the last defined value will be
used.
Parameter values supplied via the `--parameter` option will override any that are defined via
parameter files.

Template parameters that have default values can be overridden with the same mechanism:
Template parameters that have default values can be overridden with the same mechanisms:

``` bash
ktmpl example.yml --parameter MONGODB_USER carl --parameter MONGODB_PASSWORD secret
Expand All @@ -72,6 +80,7 @@ ktmpl example.yml --base64-parameter MONGODB_PASSWORD c2VjcmV0 | kubectl create
```

This can be handy when working with Kubernetes secrets, or anywhere else binary or opaque data is needed.
Values provided via parameter files are always treated as plain strings, so the `--base64-parameter` option is required for values that are already Base64-encoded.

When working with Kubernetes secrets with values that _contain_ a parameter as well as literal text, such as a config file with passwords in it, the `--secret` option is useful.
For example, using the provided [secret_example.yml](secret_example.yml) template:
Expand Down
5 changes: 0 additions & 5 deletions params.yaml

This file was deleted.

4 changes: 4 additions & 0 deletions params.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
MONGODB_PASSWORD: "secret"
MONGODB_USER: "carl"
REPLICA_COUNT: "2"
125 changes: 61 additions & 64 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,27 @@ extern crate regex;
extern crate yaml_rust as yaml;

pub use template::Template;
pub use parameter::{ParameterValue, ParameterValues};
pub use parameterfile::{ParameterFile};
pub use parameter::{ParameterValue, ParameterValues, parameter_values_from_file};
pub use secret::{Secret, Secrets};

mod parameter;
mod parameterfile;
mod processor;
mod secret;
mod template;

#[cfg(test)]
mod tests {
use super::{ParameterValue, ParameterValues, ParameterFile, Secret, Secrets, Template};
use std::fs::File;
use std::io::Read;

use super::{
ParameterValue,
ParameterValues,
Secret,
Secrets,
Template,
parameter_values_from_file,
};

#[test]
fn encode_secrets() {
Expand Down Expand Up @@ -197,55 +205,18 @@ parameters:
}

#[test]
fn parse_parameter_file() {
let parameter_file_contents = r#"
---
db_user: bob
db_password: changeme
db_port: 1234
"#;
let template_contents = r#"
---
kind: Template
apiVersion: v1
metadata:
name: param-file-example
objects:
- kind: Pod
apiVersion: v1
metadata:
name: db_app
spec:
containers:
- name: mydb
image: mydb
env:
- name: USERNAME
value: $(db_user)
- name: PASSWORD
value: $(db_password)
- name: DB_PORT
value: $(db_port)
parameters:
- name: db_user
description: Database username
required: true
parameterType: string
- name: db_password
description: Database user password
required: true
parameterType: base64
- name: db_port
description: Database port
required: true
parameterType: int
"#;
fn parameter_file() {
let mut template_file = File::open("example.yml").unwrap();
let mut template_contents = String::new();

template_file.read_to_string(&mut template_contents).unwrap();

let parameter_values = parameter_values_from_file("params.yml").unwrap();

let parameter_file = ParameterFile::from_str(parameter_file_contents.to_string()).unwrap();
let template = Template::new(
template_contents.to_string(),
parameter_file.parameters,
Some(Secrets::new()),
parameter_values,
None,
).unwrap();

let processed_template = template.process().unwrap();
Expand All @@ -254,24 +225,50 @@ parameters:
processed_template.lines().map(|l| l.trim_right()).collect::<Vec<&str>>().join("\n"),
r#"---
apiVersion: v1
kind: Pod
kind: Service
metadata:
name: db_app
name: mongodb
spec:
containers:
ports:
-
env:
-
name: USERNAME
value: bob
-
name: PASSWORD
value: "Y2hhbmdlbWU="
name: mongo
protocol: TCP
targetPort: 27017
selector:
name: mongodb
---
apiVersion: v1
kind: ReplicationController
metadata:
name: mongodb
spec:
replicas: 2
selector:
name: mongodb
template:
metadata:
creationTimestamp: ~
labels:
name: mongodb
spec:
containers:
-
name: DB_PORT
value: "1234"
image: mydb
name: mydb"#
env:
-
name: MONGODB_USER
value: carl
-
name: MONGODB_PASSWORD
value: c2VjcmV0
-
name: MONGODB_DATABASE
value: sampledb
image: "docker.io/centos/mongodb-26-centos7"
name: mongodb
ports:
-
containerPort: 27017
protocol: TCP"#
);
}
}
30 changes: 19 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ use std::process::exit;

use clap::{App, AppSettings, Arg, Values};

use ktmpl::{Template, ParameterValue, ParameterValues, ParameterFile, Secret, Secrets};
use ktmpl::{
ParameterValue,
ParameterValues,
Secret,
Secrets,
Template,
parameter_values_from_file,
};

fn main() {
if let Err(error) = real_main() {
Expand Down Expand Up @@ -66,23 +73,22 @@ fn real_main() -> Result<(), String> {
)
.arg(
Arg::with_name("parameter-file")
.help("Supplies a Yaml file defining any named parameters")
.help("Path to a YAML file with parameter values")
.next_line_help(true)
.long("parameter-file")
.short("f")
.multiple(true)
.takes_value(true)
.number_of_values(1)
.value_names(&["FILENAME"])
.value_names(&["PARAMETER_FILE"])
)
.get_matches();

let mut values = HashMap::new();

// Parse Parameter files first, passing command line parameters
// should override any values supplied via a file
if let Some(files) = matches.values_of("parameter-file") {
let params_from_file = parameter_files(files);
let params_from_file = parameter_files(files)?;

values.extend(params_from_file);
}

Expand Down Expand Up @@ -123,18 +129,20 @@ fn real_main() -> Result<(), String> {
}
}

fn parameter_files(mut param_files: Values) -> ParameterValues {
fn parameter_files(mut param_files: Values) -> Result<ParameterValues, String> {
let mut parameter_values = ParameterValues::new();

loop {
if let Some(f) = param_files.next() {
let param_file = ParameterFile::from_file(&f).unwrap();
parameter_values.extend(param_file.parameters);
if let Some(filename) = param_files.next() {
let values = parameter_values_from_file(&filename)?;

parameter_values.extend(values);
} else {
break;
}
}
parameter_values

Ok(parameter_values)
}

fn parameter_values(mut parameters: Values, base64_encoded: bool) -> ParameterValues {
Expand Down
46 changes: 45 additions & 1 deletion src/parameter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

use base64::encode;
use yaml::Yaml;
use yaml::{Yaml, YamlLoader};

#[derive(Debug)]
pub struct Parameter {
Expand Down Expand Up @@ -36,6 +39,47 @@ pub type ParamMap = HashMap<String, Parameter>;
/// A map of parameter names to user-supplied values of the parameters.
pub type ParameterValues = HashMap<String, ParameterValue>;

/// Creates a `ParameterFile` from a file.
pub fn parameter_values_from_file(file_path: &str) -> Result<ParameterValues, String> {
let mut file = File::open(file_path).map_err(|err| err.description().to_owned())?;

let mut contents = String::new();
file.read_to_string(&mut contents).map_err(|err| err.description().to_owned())?;

let docs = YamlLoader::load_from_str(&contents)
.map_err(|err| err.description().to_owned())?;

let mut parameter_values = ParameterValues::new();

for doc in docs {
match doc {
Yaml::Hash(ref hash) => {
for (key, value) in hash {
match *key {
Yaml::String(ref key_string) => {
match *value {
Yaml::String(ref value_string) => {
parameter_values.insert(
key_string.to_string(),
ParameterValue::Plain(value_string.to_string()),
);
}
_ => return Err("Parameter values in paramter files must be strings.".to_string()),
}
}
_ => return Err(
"Parameters in parameter files must be strings.".to_string()
),
}
}
}
_ => return Err("YAML documents in parameter files must be hashes.".to_string()),
}
}

Ok(parameter_values)
}

fn maybe_base64_encode(parameter_type: &Option<ParameterType>, user_value: &ParameterValue) -> String {
if parameter_type.is_none() || parameter_type.as_ref().unwrap() != &ParameterType::Base64 {
return match *user_value {
Expand Down
Loading

0 comments on commit 30c8210

Please sign in to comment.