aws_parameter_update/
lib.rs

1//! # AWS Parameter Update Library
2//!
3//! `aws_parameter_update` is a small tool used to quickly update simple AWS Parameters
4#![warn(missing_docs)]
5#![warn(missing_doc_code_examples)]
6
7use anyhow::Result;
8use log::{error, info};
9use rusoto_core::Region;
10use rusoto_ssm::SsmClient;
11use std::fs::File;
12use std::io::prelude::Read;
13use std::iter::Map;
14use yaml_rust::YamlLoader;
15
16pub use parameter::Parameter;
17
18mod parameter;
19/// Updates AWS Parameters from a YAML file
20///
21/// # File Structure
22/// The file structure for updating parameters is as follows:
23/// ```yaml
24/// - name: "new_parameter"
25///   value: "Example parameter"
26///   description: "An example of an unsecure parameter"
27///   is_secure: false
28/// - name: "new_secure_parameter"
29///   value: "$uper$ecretP@$$W0rd"
30///   description: "An example of a secure parameter"
31///   is_secure: true
32/// ```
33///
34/// # Example
35///
36/// ```should_panic because there is no file
37/// let filename = "non_existing_file.yaml";
38///
39/// match tokio_test::block_on(aws_parameter_update::update_from_file(filename)) {
40///     Ok(_) => {
41///         println!("Parameter update from file {} finished", filename);
42///     }
43///     Err(error) => {
44///         println!("Parameter updated from file {} failed: {}", filename, error);
45///     }
46/// };
47/// ```
48pub async fn update_from_file(filename: &str) -> Result<()> {
49    let parameters_from_yaml = read_parameters_yaml(filename)?;
50
51    update_parameters(parameters_from_yaml).await
52}
53
54/// Updates AWS Parameter from calling function input
55///
56/// # Example
57///
58/// ```
59/// let name = "name".to_string();
60/// let value = "value".to_string();
61/// let description = "description".to_string();
62/// let is_secure = true;
63///
64/// match tokio_test::block_on(aws_parameter_update::update_parameter(&name, &value, &description, is_secure)) {
65///     Ok(_) => {
66///         println!("Parameter update finished");
67///     }
68///     Err(error) => {
69///         println!("Parameter update failed: {}", error);
70///     }
71/// };
72/// ```
73pub async fn update_parameter(
74    name: &str,
75    value: &str,
76    description: &str,
77    is_secure: bool,
78) -> Result<()> {
79    update_parameters(vec![Parameter::new(name, value, description, is_secure)]).await
80}
81
82/// Updates AWS Parameters from calling function input
83///
84/// # Example
85///
86/// ```
87/// use aws_parameter_update::Parameter;
88///
89/// let parameters_to_update = vec![Parameter::new(
90///         "firstName",
91///         "firstValue",
92///         "firstDescription",
93///         true,
94///     ),
95///     Parameter::new(
96///         "secondName",
97///         "secondValue",
98///         "secondDescription",
99///         false,
100///     )];
101///
102/// match tokio_test::block_on(aws_parameter_update::update_parameters(parameters_to_update)) {
103///     Ok(_) => {
104///         println!("Parameter updates finished");
105///     }
106///     Err(error) => {
107///         println!("Parameter updates failed: {}", error);
108///     }
109/// };
110/// ```
111pub async fn update_parameters(parameters: Vec<Parameter>) -> Result<()> {
112    let client = SsmClient::new(Region::UsWest2);
113
114    for parameter in parameters {
115        match parameter.update(&client).await {
116            Ok(parameter_name) => info!("Parameter {} processed", parameter_name),
117            Err(_error) => error!("Parameter not updated"),
118        }
119    }
120
121    info!("Parameter update finished running");
122    Ok(())
123}
124
125fn read_parameters_yaml(filename: &str) -> Result<Vec<Parameter>> {
126    let mut file = File::open(filename).expect("Unable to open parameter input file");
127    let mut contents = String::new();
128
129    file.read_to_string(&mut contents)
130        .expect("Unable to read parameter input file");
131
132    // YamlLoader returns a "doc" which can have multiple YAML files in it,
133    // hence the two iterators and the flattening
134    let parameters = YamlLoader::load_from_str(&contents)?
135        .into_iter()
136        .map(|yaml_document| -> Map<_, _> {
137            yaml_document.into_iter().map(|param| -> Parameter {
138                Parameter::new(
139                    param["name"].as_str().expect("name missing"),
140                    param["value"].as_str().expect("value missing"),
141                    param["description"].as_str().expect("description missing"),
142                    param["is_secure"].as_bool().expect("is_secure missing"),
143                )
144            })
145        })
146        .flatten()
147        .collect::<Vec<_>>();
148
149    info!("Parameters YAML loaded");
150    Ok(parameters)
151}
152
153mod tests {
154    #[tokio::test]
155    #[should_panic]
156    async fn test_update_from_file() {
157        let result = crate::update_from_file("missing_file.yaml").await;
158
159        assert!(result.is_err());
160    }
161
162    #[tokio::test]
163    async fn test_update_parameter() {
164        let result = crate::update_parameter("name", "value", "description", true).await;
165
166        assert!(result.is_ok());
167    }
168
169    #[tokio::test]
170    async fn test_update_parameters() {
171        let result = crate::update_parameters(vec![crate::Parameter::new(
172            "name",
173            "value",
174            "description",
175            true,
176        )])
177        .await;
178
179        assert!(result.is_ok());
180    }
181}