diff --git a/Cargo.lock b/Cargo.lock index 8280d9c13..1dd70dd6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,7 +448,7 @@ dependencies = [ "libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", @@ -664,7 +664,7 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", @@ -884,6 +884,11 @@ name = "linked-hash-map" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "linked-hash-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lock_api" version = "0.1.5" @@ -1063,6 +1068,16 @@ dependencies = [ "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "openapiv3" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "openssl" version = "0.10.16" @@ -1190,6 +1205,7 @@ dependencies = [ "hyper 0.12.20 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openapiv3 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "prometheus 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1411,7 +1427,7 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1434,7 +1450,6 @@ dependencies = [ "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-threadpool 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1562,6 +1577,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" @@ -1594,6 +1612,17 @@ dependencies = [ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_yaml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha1" version = "0.6.0" @@ -2290,6 +2319,14 @@ dependencies = [ "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "yaml-rust" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7dbbe033994ae2198a18517c7132d952a29fb1db44249a1234779da7c50f4698" "checksum actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c616db5fa4b0c40702fb75201c2af7f8aa8f3a2e2c1dda3b0655772aa949666" @@ -2381,6 +2418,7 @@ dependencies = [ "checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" "checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b" "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" +"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" @@ -2400,6 +2438,7 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" "checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" +"checksum openapiv3 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86625b0500f70f1da6cb869605cfd99609dc52875aaaa33bc22258fd2bf75ed4" "checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" @@ -2437,7 +2476,7 @@ dependencies = [ "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum reqwest 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "519ae6326d2e2863bd9e03cf5bd57758a330076a4b2be42eb00cea9a35f4084a" +"checksum reqwest 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0e60f169af3915c294818d55dde549f00d2966cef36d6c5e7255d75df3f2b16f" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" @@ -2457,6 +2496,7 @@ dependencies = [ "checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" "checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" +"checksum serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0887a8e097a69559b56aa2526bf7aff7c3048cf627dff781f0b56a6001534593" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" "checksum signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" @@ -2534,3 +2574,4 @@ dependencies = [ "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +"checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" diff --git a/policy-engine/Cargo.toml b/policy-engine/Cargo.toml index 17e37e8fb..8927f513c 100644 --- a/policy-engine/Cargo.toml +++ b/policy-engine/Cargo.toml @@ -18,3 +18,4 @@ prometheus = "^0.4.2" semver = "^0.9.0" serde_json = "^1.0.22" structopt = "^0.2.10" +openapiv3 = "0.1" \ No newline at end of file diff --git a/policy-engine/src/main.rs b/policy-engine/src/main.rs index be263a62e..57aaf0e6f 100644 --- a/policy-engine/src/main.rs +++ b/policy-engine/src/main.rs @@ -11,6 +11,7 @@ extern crate futures; extern crate hyper; #[macro_use] extern crate lazy_static; +#[macro_use] extern crate log; #[macro_use] extern crate prometheus; @@ -18,10 +19,12 @@ extern crate semver; extern crate serde_json; #[macro_use] extern crate structopt; +extern crate openapiv3; mod config; mod graph; mod metrics; +mod openapi; use actix_web::{http::Method, middleware::Logger, server, App}; use failure::Error; @@ -65,6 +68,7 @@ fn main() -> Result<(), Error> { .middleware(Logger::default()) .prefix(app_prefix) .route("/v1/graph", Method::GET, graph::index) + .route("/v1/openapi", Method::GET, openapi::index) }) .bind((opts.address, opts.port))? .start(); diff --git a/policy-engine/src/openapi.rs b/policy-engine/src/openapi.rs new file mode 100644 index 000000000..1febaa6ac --- /dev/null +++ b/policy-engine/src/openapi.rs @@ -0,0 +1,65 @@ +use actix_web::{HttpRequest, HttpResponse}; +use openapiv3::OpenAPI; +use AppState; + +const SPEC: &str = include_str!("openapiv3.json"); + +pub(crate) fn index(req: HttpRequest) -> HttpResponse { + let path_prefix = &req.state().path_prefix; + + let mut spec_object: OpenAPI = match serde_json::from_str(SPEC) { + Ok(o) => o, + Err(e) => { + let e = format_err!("Could not deserialize to OpenAPI object: {}", e); + error!("{}", e); + return HttpResponse::from_error(e.into()); + } + }; + + // Prefix all paths with `path_prefix` + spec_object.paths = rewrite_paths(spec_object.paths, path_prefix); + + match serde_json::to_string(&spec_object) { + Ok(s) => HttpResponse::from(s), + Err(e) => { + let e = format_err!("Could not serialize OpenAPI object: {}", e); + error!("{}", e); + HttpResponse::from_error(e.into()) + } + } +} + +fn rewrite_paths(paths: openapiv3::Paths, path_prefix: &str) -> openapiv3::Paths { + paths + .into_iter() + .map(|(path, path_item)| { + let new_path = format!("{}{}", path_prefix, &path); + trace!("Rewrote path {} -> {} ", &path, &new_path); + (new_path, path_item) + }) + .collect() +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_rewrite_paths() { + use super::{rewrite_paths, SPEC}; + use openapiv3::OpenAPI; + + let prefix = "/test_prefix"; + let spec_object: OpenAPI = serde_json::from_str(SPEC).expect("couldn't parse JSON file"); + + let paths_before = spec_object.paths; + let paths_after = rewrite_paths(paths_before.clone(), &prefix); + + paths_before.iter().zip(paths_after.iter()).for_each( + |((path_before, _), (path_after, _))| { + assert_ne!(path_after, path_before); + assert_eq!(path_after, &format!("{}{}", prefix, path_before)); + assert!(path_after.as_str().starts_with(prefix)); + }, + ); + } +} diff --git a/policy-engine/src/openapiv3.json b/policy-engine/src/openapiv3.json new file mode 100644 index 000000000..6d86ca88a --- /dev/null +++ b/policy-engine/src/openapiv3.json @@ -0,0 +1,103 @@ +{ + "openapi": "3.0.2", + "info": { + "version": "0.0.0", + "title": "OpenShift Cincinnati Policy-Engine", + "license": { + "name": "Apache2" + }, + "contact": {} + }, + "servers": [], + "paths": { + "/v1/graph": { + "get": { + "summary": "Get the update graph", + "operationId": "getGraph", + "responses": { + "200": { + "description": "An update graph", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Graph" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Graph": { + "properties": { + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Node" + } + }, + "edges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Edge" + } + } + } + }, + "Node": { + "required": [ + "version", + "payload", + "metadata" + ], + "properties": { + "version": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "metadata": { + "type": "string" + } + } + }, + "Edge": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + }, + "security": [] +} \ No newline at end of file