Skip to content

Commit

Permalink
WIP: fixes #125
Browse files Browse the repository at this point in the history
  • Loading branch information
schrieveslaach committed Jan 9, 2024
1 parent 40c46a5 commit 1106866
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 406 deletions.
440 changes: 211 additions & 229 deletions api/Cargo.lock

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ kube = { version = "0.84", default-features = false, features = ["client", "deri
lazy_static = "1.4"
log = "0.4"
multimap = "0.9"
oci-distribution = "0.10"
pest = "2.6"
pest_derive = "2.6"
regex = "1.9"
Expand All @@ -51,10 +52,6 @@ uuid = { version = "1.3", features = ["serde", "v4"] }
yansi = "0.5"


[dependencies.dkregistry]
git = "https://github.com/camallo/dkregistry-rs.git"
rev = "0642b1c"

[dependencies.shiplift]
git = "https://github.com/softprops/shiplift.git"
rev = "3a7c1dc3ae388b6a9f0a8f724fabff30953bcc5b"
Expand Down
38 changes: 22 additions & 16 deletions api/src/apps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ use crate::deployment::deployment_unit::DeploymentUnitBuilder;
use crate::infrastructure::Infrastructure;
use crate::models::service::{ContainerType, Service, ServiceStatus};
use crate::models::{AppName, AppStatusChangeId, LogChunk, ServiceConfig};
use crate::registry::ImagesServiceError;
use crate::registry::Registry;
use crate::registry::RegistryError;
use chrono::{DateTime, FixedOffset};
use handlebars::RenderError;
pub use host_meta_cache::new as host_meta_crawling;
pub use host_meta_cache::HostMetaCache;
pub use host_meta_cache::HostMetaCrawler;
use multimap::MultiMap;
pub use routes::{apps_routes, delete_app_sync};
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -275,9 +275,15 @@ impl AppsService {

let deployment_unit_builder = DeploymentUnitBuilder::init(app_name.clone(), configs)
.extend_with_config(&self.config)
.extend_with_templating_only_service_configs(configs_for_templating)
.resolve_image_manifest(&self.config)
.await?
.extend_with_templating_only_service_configs(configs_for_templating);

let images = deployment_unit_builder.images();
let image_infos = Registry::new(&self.config)
.resolve_image_infos(&images)
.await?;

let deployment_unit_builder = deployment_unit_builder
.extend_with_image_infos(image_infos)
.apply_templating()?
.apply_hooks(&self.config)
.await?;
Expand Down Expand Up @@ -393,7 +399,7 @@ pub enum AppsServiceError {
#[fail(display = "Invalid configuration (invalid template): {}", error)]
InvalidTemplateFormat { error: Arc<RenderError> },
#[fail(display = "Unable to resolve information about image: {}", error)]
UnableToResolveImage { error: ImagesServiceError },
UnableToResolveImage { error: RegistryError },
#[fail(display = "Invalid deployment hook.")]
InvalidDeploymentHook,
}
Expand Down Expand Up @@ -422,8 +428,8 @@ impl From<RenderError> for AppsServiceError {
}
}

impl From<ImagesServiceError> for AppsServiceError {
fn from(error: ImagesServiceError) -> Self {
impl From<RegistryError> for AppsServiceError {
fn from(error: RegistryError) -> Self {
AppsServiceError::UnableToResolveImage { error }
}
}
Expand Down Expand Up @@ -704,12 +710,12 @@ Log msg 3 of service-a of app master
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/openid:latest'
image = 'keycloak/keycloak:23.0'
[companions.db]
serviceName = 'db'
type = 'service'
image = 'private.example.com/library/db:latest'
image = 'postgres:16.1'
"#
);
let infrastructure = Box::new(Dummy::new());
Expand Down Expand Up @@ -742,12 +748,12 @@ Log msg 3 of service-a of app master
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/openid:latest'
image = 'keycloak/keycloak:23.0'
[companions.db]
serviceName = 'db'
type = 'service'
image = 'private.example.com/library/db:latest'
image = 'postgres:16.1'
"#
);
let infrastructure = Box::new(Dummy::new());
Expand Down Expand Up @@ -785,7 +791,7 @@ Log msg 3 of service-a of app master
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/openid:latest'
image = 'keycloak/keycloak:23.0'
env = [ "VAR_1=abcd", "VAR_2=1234" ]
[companions.openid.labels]
Expand Down Expand Up @@ -850,7 +856,7 @@ Log msg 3 of service-a of app master
[companions.openid]
serviceName = 'openid'
type = 'application'
image = 'private.example.com/library/openid:latest'
image = 'keycloak/keycloak:23.0'
env = [ """SERVICES={{~#each services~}}{{name}},{{~/each~}}""" ]
"#
);
Expand Down Expand Up @@ -980,15 +986,15 @@ Log msg 3 of service-a of app master
[companions.db1]
serviceName = 'db1'
type = 'service'
image = 'private.example.com/library/db:latest'
image = 'postgres:16.1'
[companions.db1.volumes]
'/etc/mysql1/my.cnf' = 'EFGH'
[companions.db2]
serviceName = 'db2'
type = 'service'
image = 'private.example.com/library/db:latest'
image = 'postgres:16.1'
[companions.db2.files]
'/etc/mysql2/my.cnf' = 'ABCD'
Expand Down
106 changes: 104 additions & 2 deletions api/src/apps/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,17 @@ impl<'r> Responder<'r, 'static> for ServiceStatusResponse {

impl From<AppsError> for HttpApiError {
fn from(error: AppsError) -> Self {
let status = match error {
let status = match &error {
AppsError::UnableToResolveImage { error } => match error {
crate::registry::RegistryError::ImageNotFound { .. } => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
},
AppsError::AppNotFound { .. } => StatusCode::NOT_FOUND,
AppsError::AppIsInDeployment { .. } => StatusCode::CONFLICT,
AppsError::AppIsInDeletion { .. } => StatusCode::CONFLICT,
AppsError::InfrastructureError { .. }
| AppsError::InvalidServerConfiguration { .. }
| AppsError::InvalidTemplateFormat { .. }
| AppsError::UnableToResolveImage { .. }
| AppsError::InvalidDeploymentHook => {
error!("Internal server error: {}", error);
StatusCode::INTERNAL_SERVER_ERROR
Expand Down Expand Up @@ -755,4 +758,103 @@ mod tests {
assert_eq!(response.status(), Status::BadRequest);
}
}

mod http_api_error {
use super::super::*;
use crate::{apps::AppsError, registry::RegistryError};
use assert_json_diff::assert_json_eq;
use rocket::local::asynchronous::Client;

#[tokio::test]
async fn image_registry_authentication_error() {
#[get("/")]
fn image_auth_failed() -> HttpResult<&'static str> {
Err(AppsError::UnableToResolveImage {
error: RegistryError::AuthenticationFailure {
image: String::from("private-registry.example.com/_/postgres"),
failure: String::from("403: invalid user name and password"),
},
}
.into())
}
let rocket = rocket::build().mount("/", routes![image_auth_failed]);

let client = Client::tracked(rocket).await.expect("valid rocket");
let response = client.get("/").dispatch().await;

assert_eq!(response.status(), Status::InternalServerError);

let body = response.into_string().await.unwrap();
assert_json_eq!(
serde_json::from_str::<serde_json::Value>(&body).unwrap(),
serde_json::json!({
"type": "https://httpstatuses.com/500",
"status": 500,
"title": "Internal Server Error",
"detail": "Unable to resolve information about image: Cannot resolve image private-registry.example.com/_/postgres due to authentication failure: 403: invalid user name and password"
})
);
}

#[tokio::test]
async fn image_registry_unexpected_error() {
#[get("/")]
fn image_not_found() -> HttpResult<&'static str> {
Err(AppsError::UnableToResolveImage {
error: RegistryError::UnexpectedError {
image: String::from("private-registry.example.com/_/postgres"),
internal_message: String::from("unexpected"),
},
}
.into())
}
let rocket = rocket::build().mount("/", routes![image_not_found]);

let client = Client::tracked(rocket).await.expect("valid rocket");
let response = client.get("/").dispatch().await;

assert_eq!(response.status(), Status::InternalServerError);

let body = response.into_string().await.unwrap();
assert_json_eq!(
serde_json::from_str::<serde_json::Value>(&body).unwrap(),
serde_json::json!({
"type": "https://httpstatuses.com/500",
"status": 500,
"title": "Internal Server Error",
"detail": "Unable to resolve information about image: Unexpected docker registry error when resolving manifest for private-registry.example.com/_/postgres: unexpected"
})
);
}

#[tokio::test]
async fn image_registry_not_found_error() {
#[get("/")]
fn image_not_found() -> HttpResult<&'static str> {
Err(AppsError::UnableToResolveImage {
error: RegistryError::ImageNotFound {
image: String::from("private-registry.example.com/_/postgres"),
},
}
.into())
}
let rocket = rocket::build().mount("/", routes![image_not_found]);

let client = Client::tracked(rocket).await.expect("valid rocket");
let response = client.get("/").dispatch().await;

assert_eq!(response.status(), Status::NotFound);

let body = response.into_string().await.unwrap();
assert_json_eq!(
serde_json::from_str::<serde_json::Value>(&body).unwrap(),
serde_json::json!({
"type": "https://httpstatuses.com/404",
"status": 404,
"title": "Not Found",
"detail": "Unable to resolve information about image: Cannot find image private-registry.example.com/_/postgres"
})
);
}
}
}
Loading

0 comments on commit 1106866

Please sign in to comment.