Skip to content
Permalink
Browse files

Add replicateFrom parameter to POST of /apps/{appName}

This feauter allows to run previews of multiple development branches.
For example, if your team supports multiple development branches of
the services, PREvant can serve to deploy previews that reflect the
development status of these branches.
  • Loading branch information...
schrieveslaach committed Mar 8, 2019
1 parent 312589b commit 9b01ed32cce5a7e1fde28468853f1a4c31569af9

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -15,6 +15,7 @@ futures = "0.1"
handlebars = "1.1"
hyper = "0.10"
log = "0.4"
mockers_derive = "0.13.1"
multimap = "0.4"
serde = "1.0"
serde_derive = "1.0"
@@ -47,3 +48,6 @@ rev = "1d72ffee98b88321418181998225c700904be414"
version = "0.9.7"
default-features = false
features = ["rustls-tls"]

[dev-dependencies]
mockers = "0.13.1"
@@ -45,14 +45,10 @@ paths:
properties:
"^[a-zA-Z0-9_-]":
$ref: '#/components/schemas/Ticket'
'500':
description: Server error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
'503':
'204':
description: 'No ticket system configuration'
'500':
description: Internal server error
content:
application/problem+json:
schema:
@@ -68,6 +64,12 @@ paths:
type: string
required: true
description: Name of review app to create
- in: query
name: replicateFrom
schema:
type: string
default: 'master'
description: The application name that will be used to replicate from.
requestBody:
description: Information of review app to create
required: true
@@ -28,12 +28,13 @@ use crate::models::request_info::RequestInfo;
use crate::models::service::{Service, ServiceConfig};
use crate::services::apps_service::AppsService;
use crate::services::config_service::Config;
use crate::services::docker::docker_infrastructure::DockerInfrastructure;
use http_api_problem::HttpApiProblem;
use multimap::MultiMap;
use rocket::data::{self, FromDataSimple};
use rocket::http::RawStr;
use rocket::http::Status;
use rocket::request::Request;
use rocket::request::{Form, Request};
use rocket::Data;
use rocket::Outcome::{Failure, Success};
use rocket::State;
@@ -45,7 +46,7 @@ pub fn apps(
config_state: State<Config>,
request_info: RequestInfo,
) -> Result<Json<MultiMap<String, Service>>, HttpApiProblem> {
let apps_service = AppsService::new(&config_state)?;
let apps_service = AppsService::new(&config_state, Box::new(DockerInfrastructure::new()))?;
let mut apps = apps_service.get_apps()?;

for (_, services) in apps.iter_all_mut() {
@@ -63,7 +64,7 @@ pub fn delete_app(
config_state: State<Config>,
request_info: RequestInfo,
) -> Result<Json<Vec<Service>>, HttpApiProblem> {
let apps_service = AppsService::new(&config_state)?;
let apps_service = AppsService::new(&config_state, Box::new(DockerInfrastructure::new()))?;
let mut services = apps_service.delete_app(&app_name.to_string())?;

for service in services.iter_mut() {
@@ -74,19 +75,23 @@ pub fn delete_app(
}

#[post(
"/apps/<app_name>",
"/apps/<app_name>?<create_app_form..>",
format = "application/json",
data = "<service_configs_data>"
)]
pub fn create_app(
app_name: &RawStr,
config_state: State<Config>,
create_app_form: Form<CreateAppOptions>,
request_info: RequestInfo,
service_configs_data: ServiceConfigsData,
) -> Result<Json<Vec<Service>>, HttpApiProblem> {
let apps_service = AppsService::new(&config_state)?;
let mut services = apps_service
.create_or_update(&app_name.to_string(), &service_configs_data.service_configs)?;
let apps_service = AppsService::new(&config_state, Box::new(DockerInfrastructure::new()))?;
let mut services = apps_service.create_or_update(
&app_name.to_string(),
create_app_form.replicate_from().clone(),
&service_configs_data.service_configs,
)?;

for service in services.iter_mut() {
service.set_base_url(request_info.get_base_url());
@@ -95,6 +100,18 @@ pub fn create_app(
Ok(Json(services))
}

#[derive(FromForm)]
pub struct CreateAppOptions {
#[form(field = "replicateFrom")]
replicate_from: Option<String>,
}

impl CreateAppOptions {
fn replicate_from(&self) -> &Option<String> {
&self.replicate_from
}
}

pub struct ServiceConfigsData {
service_configs: Vec<ServiceConfig>,
}
@@ -24,7 +24,7 @@
* =========================LICENSE_END==================================
*/

#![feature(proc_macro_hygiene, decl_macro, try_from)]
#![feature(custom_attribute, proc_macro_hygiene, decl_macro, try_from)]

#[macro_use]
extern crate failure;
@@ -52,19 +52,17 @@ mod webhooks;

#[get("/")]
fn index() -> CacheResponse<Option<NamedFile>> {
CacheResponse::Public {
CacheResponse::Private {
responder: NamedFile::open(Path::new("frontend/index.html")).ok(),
max_age: 60 * 60, // cached for seconds
must_revalidate: false,
}
}

#[get("/<path..>")]
fn files(path: PathBuf) -> CacheResponse<Option<NamedFile>> {
CacheResponse::Public {
CacheResponse::Private {
responder: NamedFile::open(Path::new("frontend/").join(path)).ok(),
max_age: 60 * 60, // cached for seconds
must_revalidate: false,
}
}

@@ -31,7 +31,7 @@ use std::collections::BTreeMap;
use std::str::FromStr;
use url::Url;

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Service {
app_name: String,
service_name: String,
@@ -40,7 +40,7 @@ pub struct Service {
base_url: Option<Url>,
}

#[derive(Clone, Deserialize, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ServiceConfig {
service_name: String,
@@ -56,7 +56,7 @@ pub struct ServiceConfig {
port: u16,
}

#[derive(Clone, Deserialize, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub enum Image {
Named {
image_repository: String,
@@ -26,7 +26,6 @@

use crate::models::service::{ContainerType, Service, ServiceConfig};
use crate::services::config_service::{Config, ConfigError};
use crate::services::docker::docker_infrastructure::DockerInfrastructure;
use crate::services::service_templating::{
apply_templating_for_application_companion, apply_templating_for_service_companion,
};
@@ -45,10 +44,13 @@ pub struct AppsService<'a> {
}

impl<'a> AppsService<'a> {
pub fn new(config: &'a Config) -> Result<AppsService, AppsServiceError> {
pub fn new(
config: &'a Config,
infrastructure: Box<dyn Infrastructure>,
) -> Result<AppsService, AppsServiceError> {
Ok(AppsService {
config,
infrastructure: Box::new(DockerInfrastructure::new()),
infrastructure,
})
}

@@ -64,26 +66,28 @@ impl<'a> AppsService<'a> {
/// - the replications from the running template application (e.g. master)
/// - the application companions (see README)
/// - the service companions (see README)
///
/// # Arguments
/// * `replicate_from` - The application name that is used as a template.
pub fn create_or_update(
&self,
app_name: &String,
replicate_from: Option<String>,
service_configs: &Vec<ServiceConfig>,
) -> Result<Vec<Service>, AppsServiceError> {
let mut configs: Vec<ServiceConfig> = service_configs.clone();

if "master" != app_name {
let replicate_from_app_name = replicate_from.unwrap_or_else(|| String::from("master"));
if &replicate_from_app_name != app_name {
for config in self
.infrastructure
.get_configs_of_app(&String::from("master"))?
.get_configs_of_app(&replicate_from_app_name)?
.iter()
.filter(|config| {
match service_configs
service_configs
.iter()
.find(|c| c.service_name() == config.service_name())
{
None => true,
Some(_) => false,
}
.is_none()
})
{
let mut replicated_config = config.clone();
@@ -146,13 +150,10 @@ impl<'a> AppsService<'a> {
.get_configs_of_app(app_name)?
.into_iter()
.filter(|config| {
match service_configs
service_configs
.iter()
.find(|c| c.service_name() == config.service_name())
{
None => true,
Some(_) => false,
}
.is_none()
})
{
configs_for_templating.push(config);
@@ -248,3 +249,49 @@ impl From<ImagesServiceError> for AppsServiceError {
AppsServiceError::UnableToResolveImage { error }
}
}

#[cfg(test)]
mod tests {

use super::*;
use crate::models::service::Image;
use crate::services::config_service::ContainerConfig;
use mockers::matchers::any;
use mockers::Scenario;
use std::str::FromStr;

#[test]
fn should_create_app_for_master() {
let app_name = String::from("master");
let services = vec![ServiceConfig::new(
String::from("service-a"),
Image::from_str(
"sha256:541b21b43bdd8f1547599d0350713d82c74c9a72c13cfd47e742b377ea638ee2",
)
.unwrap(),
)];

let scenario = Scenario::new();
let infrastructure = scenario.create_mock_for::<Infrastructure>();
scenario.expect(
infrastructure
.get_configs_of_app_call(any::<&String>())
.and_return(Ok(vec![])),
);
scenario.expect(
infrastructure
.start_services_call(
any::<&String>(),
any::<&Vec<ServiceConfig>>(),
any::<&ContainerConfig>(),
)
.and_return(Ok(vec![])),
);

let config = Config::default();
let apps = AppsService::new(&config, Box::new(infrastructure)).unwrap();

apps.create_or_update(&app_name, None, &services).unwrap();
}

}
@@ -68,7 +68,7 @@ enum CompanionType {
Service,
}

#[derive(Clone, Deserialize)]
#[derive(Clone, Default, Deserialize)]
pub struct Config {
containers: Option<ContainerConfig>,
jira: Option<JiraConfig>,
@@ -149,14 +149,14 @@ impl Config {
}

impl JiraConfig {
pub fn get_host(&self) -> String {
self.host.clone()
pub fn host(&self) -> &String {
&self.host
}
pub fn get_user(&self) -> String {
self.user.clone()
pub fn user(&self) -> &String {
&self.user
}
pub fn get_password(&self) -> String {
self.password.clone()
pub fn password(&self) -> &String {
&self.password
}
}

@@ -297,10 +297,7 @@ mod tests {
&config.image().to_string(),
"docker.io/library/nginx:latest"
);
assert_eq!(
config.container_type(),
&ContainerType::ServiceCompanion
);
assert_eq!(config.container_type(), &ContainerType::ServiceCompanion);
assert_eq!(config.labels(), None);
});
}
Oops, something went wrong.

0 comments on commit 9b01ed3

Please sign in to comment.
You can’t perform that action at this time.