Permalink
Browse files

Prepare config to support volumes of companions.

  • Loading branch information...
schrieveslaach committed Dec 20, 2018
1 parent fa9b94c commit 7d431f12c590219f7186a30cb8818a86a9c5d379

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -18,11 +18,10 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
serde_yaml = "0.7"
shiplift = { git = "https://github.com/schrieveslaach/shiplift", rev = "d5ded6b6212e79d59f7116267aca9d081e760179" }
shiplift = { git = "https://github.com/schrieveslaach/shiplift", rev = "3ba3b05577cb62de65a1ec8edd824476f86fed3b" }
tokio = "0.1"
toml = "0.4"
regex = "1.0"
rocket = "0.4.0-rc.2"
rocket_codegen = "0.4.0-rc.2"
rocket_contrib = "0.4.0-rc.2"
rocket = "0.4"
rocket_contrib = "0.4"
url = "1.7"
@@ -42,8 +42,8 @@ If you want to include an OpenID provider for every application, you could use f

```toml
[[companions]]
[[companions.application]]
serviceName = 'openid'
[companions.openid]
type = 'application'
image = 'private.example.com/library/opendid:latest'
env = [ 'KEY=VALUE' ]
```
@@ -53,7 +53,7 @@ The provided values of `serviceName` and `env` can include the [handlebars synta
Additionally, you could mount files that are generated from handlebars templates (example contains a properties generation):

```toml
[companions.application.openid.volumes]
[companions.openid.volumes]
"/path/to/volume.properties" = """
remote.services={{#each services~}}
{{~#if (eq type 'instance')~}}
@@ -62,7 +62,7 @@ impl FromDataSimple for CreateOrUpdateAppCommand {
return Failure((
Status::BadRequest,
format!("Cannot read body as JSON: {:?}", err),
))
));
}
};

@@ -60,7 +60,7 @@ impl ListTicketsCommand {
None => {
return Err(ListTicketsError::Internal(String::from(
"No JIRA Configuration",
)))
)));
}
Some(jira_config) => {
let apps_service = AppsService::new()?;
@@ -23,10 +23,10 @@
* THE SOFTWARE.
* =========================LICENSE_END==================================
*/
use std::str::FromStr;

use regex::Regex;
use serde::ser::{Serialize, Serializer};
use std::collections::BTreeMap;
use std::str::FromStr;
use url::Url;

#[derive(Clone)]
@@ -47,7 +47,7 @@ pub struct ServiceConfig {
image_user: Option<String>,
image_tag: Option<String>,
env: Option<Vec<String>>,
volumes: Option<Vec<String>>,
volumes: BTreeMap<String, String>,
#[serde(skip, default = "ContainerType::default")]
container_type: ContainerType,
}
@@ -65,7 +65,7 @@ impl ServiceConfig {
image_user: None,
image_tag: None,
env,
volumes: None,
volumes: BTreeMap::new(),
container_type: ContainerType::Instance,
}
}
@@ -152,11 +152,12 @@ impl ServiceConfig {
}
}

pub fn get_volumes(&self) -> Option<Vec<String>> {
match &self.volumes {
None => None,
Some(volumes) => Some(volumes.clone()),
}
pub fn set_volumes(&mut self, volumes: &BTreeMap<String, String>) {
self.volumes = volumes.clone();
}

pub fn get_volumes(&self) -> &BTreeMap<String, String> {
&self.volumes
}
}

@@ -318,7 +319,7 @@ pub fn parse_image_string(
None => {
return Err(ServiceError::InvalidImageString {
invalid_string: String::from(image),
})
});
}
};

@@ -115,7 +115,7 @@ impl FromDataSimple for WebHookInfo {
return Failure((
Status::BadRequest,
format!("Cannot read body as JSON: {:?}", err),
))
));
}
};

@@ -23,14 +23,13 @@
* THE SOFTWARE.
* =========================LICENSE_END==================================
*/
use regex::Regex;
use serde::{de, Deserialize, Deserializer};
use std::collections::BTreeMap;
use std::convert::{From, TryFrom};
use std::fs::File;
use std::io::prelude::*;
use std::io::Error as IOError;

use multimap::MultiMap;
use regex::Regex;
use serde::{de, Deserialize, Deserializer};
use toml::de::Error as TomlError;
use toml::from_str;

@@ -53,49 +52,36 @@ pub struct JiraConfig {
#[serde(rename_all = "camelCase")]
pub struct Companion {
service_name: String,
#[serde(rename = "type")]
companion_type: CompanionType,
image: String,
env: Vec<String>,
volumes: Option<BTreeMap<String, String>>,
}

#[derive(Clone, Deserialize, PartialEq)]
enum CompanionType {
#[serde(rename = "application")]
Application,
#[serde(rename = "service")]
Service,
}

#[derive(Clone, Deserialize)]
pub struct Config {
containers: Option<ContainerConfig>,
jira: Option<JiraConfig>,
#[serde(deserialize_with = "Config::from_companions_table_array")]
companions: MultiMap<String, Companion>,
companions: Vec<BTreeMap<String, Companion>>,
}

impl Config {
fn from_companions_table_array<'de, D>(
deserializer: D,
) -> Result<MultiMap<String, Companion>, D::Error>
where
D: Deserializer<'de>,
{
let mut companions = MultiMap::new();

let companions_table_array = toml::Value::deserialize(deserializer)?;
for companions_table in companions_table_array.as_array().unwrap_or(&vec![]) {
if let Some(companions_table) = companions_table.as_table() {
for (k, v) in companions_table {
match from_str::<Companion>(&toml::to_string(v).unwrap()) {
Ok(companion) => companions.insert(k.clone(), companion),
Err(e) => warn!("Cannot parse companion config for {}. {}", k, e),
};
}
}
}

Ok(companions)
}

pub fn load() -> Result<Config, ConfigError> {
let mut f = match File::open("config.toml") {
Err(e) => {
warn!("Cannot find config file ({}) Loading default.", e);
return Ok(Config {
containers: None,
companions: MultiMap::new(),
companions: Vec::new(),
jira: None,
});
}
@@ -129,11 +115,16 @@ impl Config {
for companion in self
.companions
.iter()
.filter(|(k, _)| k.as_str() == "application")
.map(|(_, v)| v)
.flat_map(|companions| companions.values())
.filter(|companion| companion.companion_type == CompanionType::Application)
{
let mut config = ServiceConfig::try_from(companion)?;
config.set_container_type(ContainerType::ApplicationCompanion);

config.set_container_type(match &companion.companion_type {
CompanionType::Application => ContainerType::ApplicationCompanion,
CompanionType::Service => ContainerType::ServiceCompanion,
});

companions.push(config);
}

@@ -226,6 +217,10 @@ impl TryFrom<&Companion> for ServiceConfig {
config.set_image_user(&user);
config.set_image_tag(&tag);

if let Some(volumes) = &companion.volumes {
config.set_volumes(volumes);
}

Ok(config)
}
}
@@ -238,10 +233,17 @@ mod tests {
fn should_return_application_companions_as_service_configs() {
let config_str = r#"
[[companions]]
[companions.application]
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/opendid:latest'
env = [ 'KEY=VALUE' ]
[companions.nginx]
serviceName = '{{service-name}}-nginx'
type = 'service'
image = 'nginx:latest'
env = [ 'KEY=VALUE' ]
"#;

let config = from_str::<Config>(config_str).unwrap();
@@ -262,12 +264,37 @@ mod tests {
});
}

#[test]
fn should_return_application_companions_as_service_configs_with_volumes() {
let config_str = r#"
[[companions]]
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/opendid:latest'
env = [ 'KEY=VALUE' ]
[companions.openid.volumes]
'/tmp/test-1.json' = '{}'
'/tmp/test-2.json' = '{}'
"#;

let config = from_str::<Config>(config_str).unwrap();
let companion_configs = config.get_application_companion_configs().unwrap();

assert_eq!(companion_configs.len(), 1);
companion_configs.iter().for_each(|config| {
assert_eq!(config.get_volumes().len(), 2);
});
}

#[test]
fn should_return_application_companions_as_error_when_invalid_image_is_provided() {
let config_str = r#"
[[companions]]
[companions.application]
[companions.openid]
serviceName = 'openid'
type = 'application'
image = ''
env = [ 'KEY=VALUE' ]
"#;
@@ -171,9 +171,9 @@ impl DockerInfrastructure {
options.env(env.iter().map(|e| e.as_str()).collect());
}

if let Some(ref volumes) = service_config.get_volumes() {
options.volumes(volumes.iter().map(|v| v.as_str()).collect());
}
// TODO: if let Some(ref volumes) = service_config.get_volumes() {
// options.volumes(volumes.iter().map(|v| v.as_str()).collect());
// }

let traefik_frontend = format!(
"ReplacePathRegex: ^/{p1}/{p2}(.*) /$1;PathPrefix:/{p1}/{p2};",
@@ -475,7 +475,7 @@ impl TryFrom<&Container> for Service {
None => {
return Err(DockerInfrastructureError::MissingServiceNameLabel {
container_id: c.id.clone(),
})
});
}
};

@@ -484,7 +484,7 @@ impl TryFrom<&Container> for Service {
None => {
return Err(DockerInfrastructureError::MissingAppNameLabel {
container_id: c.id.clone(),
})
});
}
};

0 comments on commit 7d431f1

Please sign in to comment.