diff --git a/Cargo.lock b/Cargo.lock index 16de4c9447a..9d2e39e929c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ dependencies = [ "oak_tests 0.1.0", "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -88,7 +89,7 @@ dependencies = [ "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tonic 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -379,6 +380,7 @@ dependencies = [ "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "oak 0.1.0", + "oak_abi 0.1.0", "oak_utils 0.1.0", "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1366,6 +1368,7 @@ dependencies = [ "anyhow 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "oak_abi 0.1.0", "oak_runtime 0.1.0", "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1417,6 +1420,7 @@ dependencies = [ "oak_runtime 0.1.0", "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1541,11 +1545,6 @@ dependencies = [ "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "percent-encoding" version = "2.1.0" @@ -2792,17 +2791,6 @@ dependencies = [ "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tokio-rustls" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tokio-rustls" version = "0.13.1" @@ -2823,19 +2811,6 @@ dependencies = [ "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tokio-util" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tokio-util" version = "0.3.1" @@ -2857,36 +2832,6 @@ dependencies = [ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tonic" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "async-stream 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "async-trait 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "prost-derive 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-balance 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-load 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-make 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-futures 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tonic" version = "0.2.1" @@ -3588,7 +3533,6 @@ dependencies = [ "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" "checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92" "checksum pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" @@ -3704,13 +3648,10 @@ dependencies = [ "checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" "checksum tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" "checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" -"checksum tokio-rustls 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "141afec0978abae6573065a48882c6bae44c5cc61db9b511ac4abf6a09bfd9cc" "checksum tokio-rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" "checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" -"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" "checksum tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -"checksum tonic 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08283643b1d483eb7f3fc77069e63b5cba3e4db93514b3d45470e67f123e4e48" "checksum tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4afef9ce97ea39593992cf3fa00ff33b1ad5eb07665b31355df63a690e38c736" "checksum tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d8d21cb568e802d77055ab7fcd43f0992206de5028de95c8d3a41118d32e8e" "checksum tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62" diff --git a/docs/abi.md b/docs/abi.md index e17c9078b21..04cc05aaf5a 100644 --- a/docs/abi.md +++ b/docs/abi.md @@ -183,22 +183,23 @@ Closes the channel identified by `param[0]`. ### `node_create` Creates a new Node running the Node configuration identified by `param[0]` and -`param[1]`, using the entrypoint specified by `param[2]` and `param[3]`, -assigning the label specified by `param[4]` and `param[5]` to the newly created -Node, passing in an initial handle to the read half of a channel identified by -`param[6]`. The entrypoint name is ignored when creating non-WebAssembly Nodes. +`param[1]`, assigning the label specified by `param[2]` and `param[3]` to the +newly created Node, passing in an initial handle to the read half of a channel +identified by `param[4]`. The entrypoint name is ignored when creating +non-WebAssembly Nodes. + +The Node configuration is a serialized +[`NodeConfiguration`](/oak/proto/application.proto) protobuf message. If creating the specified Node would violate [information flow control](/docs/concepts.md#labels), returns `ERR_PERMISSION_DENIED`. -- `param[0]: usize`: Source buffer holding node configuration name -- `param[1]: usize`: Node configuration name size in bytes -- `param[2]: usize`: Source buffer holding entrypoint name -- `param[3]: usize`: Entrypoint name size in bytes -- `param[4]: usize`: Source buffer holding label -- `param[5]: usize`: Label size in bytes -- `param[6]: usize`: Handle to channel +- `param[0]: usize`: Source buffer holding serialized NodeConfiguration +- `param[1]: usize`: Serialized NodeConfiguration size in bytes +- `param[2]: usize`: Source buffer holding label +- `param[3]: usize`: Label size in bytes +- `param[4]: usize`: Handle to channel - `result[0]: u32`: Status of operation ### random_get diff --git a/docs/programming-oak.md b/docs/programming-oak.md index bf93cb4c045..c4c9ad198da 100644 --- a/docs/programming-oak.md +++ b/docs/programming-oak.md @@ -61,7 +61,8 @@ oak::entrypoint!(grpc_oak_main => |_in_channel| { config: None, model: NaiveBayes::new(), }; - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(node, grpc_channel); }); ``` @@ -108,7 +109,8 @@ Application), the easiest way to use a gRPC service implementation is to: oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = FormatServiceDispatcher::new(Node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); } ``` @@ -228,37 +230,21 @@ be serialized into a binary file. The Application first needs to specify a template configuration file: -[embedmd]:# (../examples/hello_world/config/config.textproto /node_configs.*/ /initial_entrypoint_name.*/) +[embedmd]:# (../examples/hello_world/config/config.textproto) ```textproto -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "translator" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" ``` -The `module_bytes: ""` means that this value will be filled with -WebAssembly module bytes after serialization using the +The `wasm_modules` field (not shown above because empty in the template +configuration) will be filled with WebAssembly module bytes after serialization +using the [_Application Configuration Serializer_](../oak/common/app_config_serializer.cc), as follows: @@ -421,7 +407,8 @@ to the room: [embedmd]:# (../examples/chat/module/rust/src/lib.rs Rust /.*channel_create\(\)/ /\}$/) ```Rust let (wh, rh) = oak::channel_create().unwrap(); - oak::node_create("app", "backend_oak_main", rh).expect("could not create node"); + oak::node_create(&oak::node_config::wasm("app", "backend_oak_main"), rh) + .expect("could not create node"); oak::channel_close(rh.handle).expect("could not close channel"); Room { sender: oak::io::Sender::new(wh), @@ -539,7 +526,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = TranslatorDispatcher::new(Node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); ``` @@ -552,20 +540,32 @@ use of the functionality of the Oak Runtime. ### Testing Multi-Node Applications It's also possible to test an Oak Application that's built from multiple Nodes, -using `oak_runtime::application_configuration` to create an application -configuration and then `oak_runtime::Runtime::configure_and_run(configuration)` -to configure and run the Runtime. +by defining an appropriate `ApplicationConfiguration` instance and then +`oak_runtime::Runtime::configure_and_run(application_configuration, ...)` to +configure and run the Runtime. -[embedmd]:# (../examples/abitest/tests/src/tests.rs Rust / +let configuration =/ /configure_and_run.*/) +[embedmd]:# (../examples/abitest/tests/src/tests.rs Rust / +let application_configuration =/ /unable to configure runtime.*/) ```Rust - let configuration = oak_runtime::application_configuration( - build_wasm().expect("failed to build wasm modules"), - LOG_CONFIG_NAME, - FRONTEND_CONFIG_NAME, - FRONTEND_ENTRYPOINT_NAME, - ); + let application_configuration = ApplicationConfiguration { + wasm_modules: build_wasm().expect("failed to build wasm modules"), + initial_node_configuration: Some(oak::node_config::wasm( + FRONTEND_MODULE_NAME, + FRONTEND_ENTRYPOINT_NAME, + )), + }; let (runtime, entry_channel) = oak_runtime::configure_and_run( + application_configuration, + oak_runtime::RuntimeConfiguration::default(), + oak_runtime::GrpcConfiguration { + grpc_server_tls_identity: None, + // Some of the tests require a gRPC client, so we populate the required certificate with + // an invalid value here, even though it will still fail when instantiating the actual + // gRPC client. + grpc_client_root_tls_certificate: Some(Certificate::from_pem("invalid-cert")), + }, + ) + .expect("unable to configure runtime with test wasm"); ``` diff --git a/examples/abitest/abitest_common/src/lib.rs b/examples/abitest/abitest_common/src/lib.rs index bd12668a17f..83cf3c2ca69 100644 --- a/examples/abitest/abitest_common/src/lib.rs +++ b/examples/abitest/abitest_common/src/lib.rs @@ -30,6 +30,3 @@ impl oak::io::Encodable for InternalMessage { Ok(oak::io::Message { bytes, handles }) } } - -// Expected name for log node config. -pub const LOG_CONFIG_NAME: &str = "logging-config"; diff --git a/examples/abitest/config/BUILD b/examples/abitest/config/BUILD index f3156ee709b..35745c9e362 100644 --- a/examples/abitest/config/BUILD +++ b/examples/abitest/config/BUILD @@ -25,8 +25,8 @@ exports_files(srcs = glob(["*.textproto"])) serialized_config( name = "config", modules = { - "frontend-config": "//:target/wasm32-unknown-unknown/release/abitest_0_frontend.wasm", - "backend-config": "//:target/wasm32-unknown-unknown/release/abitest_1_backend.wasm", + "frontend_module": "//:target/wasm32-unknown-unknown/release/abitest_0_frontend.wasm", + "backend_module": "//:target/wasm32-unknown-unknown/release/abitest_1_backend.wasm", }, textproto = ":config.textproto", ) diff --git a/examples/abitest/config/config.textproto b/examples/abitest/config/config.textproto index 16d6e97e2a0..d3b05877690 100644 --- a/examples/abitest/config/config.textproto +++ b/examples/abitest/config/config.textproto @@ -1,60 +1,7 @@ -node_configs { - name: "frontend-config" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "frontend_module" + wasm_entrypoint_name: "grpc_frontend_oak_main" } } -node_configs { - name: "backend-config" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "logging-config" - log_config {} -} -node_configs { - name: "storage" - storage_config { - address: "localhost:7867" - } -} -node_configs { - name: "absent-storage" - storage_config { - address: "test.invalid:9999" - } -} -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "grpc-client" - grpc_client_config { - uri: "https://localhost:7878" - address: "localhost:7878" - } -} -node_configs { - name: "absent-grpc-client" - grpc_client_config { - uri: "https://test.invalid:9999" - address: "test.invalid:9999" - } -} -node_configs { - name: "roughtime-client" - roughtime_client_config {} -} -node_configs { - name: "roughtime-misconfig" - roughtime_client_config { - min_overlapping_intervals: 99 - } -} -initial_node_config_name: "frontend-config" -initial_entrypoint_name: "grpc_frontend_oak_main" diff --git a/examples/abitest/module_0/rust/src/lib.rs b/examples/abitest/module_0/rust/src/lib.rs index 4f16ba6e59f..c9d0ff58606 100644 --- a/examples/abitest/module_0/rust/src/lib.rs +++ b/examples/abitest/module_0/rust/src/lib.rs @@ -18,12 +18,17 @@ pub mod proto { include!(concat!(env!("OUT_DIR"), "/oak.examples.abitest.rs")); } -use abitest_common::{InternalMessage, LOG_CONFIG_NAME}; +use abitest_common::InternalMessage; use byteorder::WriteBytesExt; use expect::{expect, expect_eq, expect_matches}; use log::{debug, error, info, trace, warn}; use oak::{grpc, ChannelReadStatus, OakError, OakStatus}; -use oak_abi::label::Label; +use oak_abi::{ + label::Label, + proto::oak::application::{ + NodeConfiguration, RoughtimeClientConfiguration, StorageProxyConfiguration, + }, +}; use prost::Message; use proto::{ AbiTestRequest, AbiTestResponse, GrpcTestRequest, GrpcTestResponse, OakAbiTestService, @@ -34,8 +39,8 @@ use std::{collections::HashMap, convert::TryInto}; const BACKEND_COUNT: usize = 3; -const FRONTEND_CONFIG_NAME: &str = "frontend-config"; -const BACKEND_CONFIG_NAME: &str = "backend-config"; +const FRONTEND_MODULE_NAME: &str = "frontend_module"; +const BACKEND_MODULE_NAME: &str = "backend_module"; const BACKEND_ENTRYPOINT_NAME: &str = "backend_oak_main"; const STORAGE_NAME_PREFIX: &str = "abitest"; @@ -64,13 +69,15 @@ impl FrontendNode { // Second, start an ephemeral Node which also loses channels. let (wh, rh) = oak::channel_create().unwrap(); - oak::node_create(FRONTEND_CONFIG_NAME, "channel_loser", rh) - .expect("failed to create channel_loser ephemeral Node"); + oak::node_create( + &oak::node_config::wasm(FRONTEND_MODULE_NAME, "channel_loser"), + rh, + ) + .expect("failed to create channel_loser ephemeral Node"); oak::channel_close(wh.handle).unwrap(); oak::channel_close(rh.handle).unwrap(); } - oak::logger::init(log::Level::Debug, LOG_CONFIG_NAME) - .expect("could not initialize logging node"); + oak::logger::init(log::Level::Debug).expect("could not initialize logging node"); // Create backend node instances. let mut backend_out = Vec::with_capacity(BACKEND_COUNT); @@ -78,8 +85,11 @@ impl FrontendNode { for i in 0..BACKEND_COUNT { let (write_handle, read_handle) = oak::channel_create().expect("could not create channel"); - oak::node_create(BACKEND_CONFIG_NAME, BACKEND_ENTRYPOINT_NAME, read_handle) - .expect("could not create node"); + oak::node_create( + &oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME), + read_handle, + ) + .expect("could not create node"); oak::channel_close(read_handle.handle).expect("could not close channel"); backend_out.push(write_handle); @@ -102,15 +112,30 @@ impl FrontendNode { FrontendNode { backend_out, backend_in, - storage: oak::storage::Storage::default(), - absent_storage: oak::storage::Storage::new("absent-storage"), + storage: oak::storage::Storage::new(&StorageProxyConfiguration { + address: "https://localhost:7867".to_string(), + }), + absent_storage: oak::storage::Storage::new(&StorageProxyConfiguration { + address: "https://test.invalid:9999".to_string(), + }), storage_name: storage_name.as_bytes().to_vec(), - grpc_service: oak::grpc::client::Client::new("grpc-client", "ignored") - .map(OakAbiTestServiceClient), - absent_grpc_service: oak::grpc::client::Client::new("absent-grpc-client", "ignored") - .map(OakAbiTestServiceClient), - roughtime: oak::roughtime::Roughtime::new("roughtime-client"), - misconfigured_roughtime: oak::roughtime::Roughtime::new("roughtime-misconfig"), + grpc_service: oak::grpc::client::Client::new(&oak::node_config::grpc_client( + "https://localhost:7878", + )) + .map(OakAbiTestServiceClient), + absent_grpc_service: oak::grpc::client::Client::new(&oak::node_config::grpc_client( + "https://test.invalid:9999", + )) + .map(OakAbiTestServiceClient), + roughtime: oak::roughtime::Roughtime::new(&RoughtimeClientConfiguration { + ..Default::default() + }), + misconfigured_roughtime: oak::roughtime::Roughtime::new( + &RoughtimeClientConfiguration { + min_overlapping_intervals: 99, + ..Default::default() + }, + ), } } } @@ -134,7 +159,8 @@ pub extern "C" fn grpc_frontend_oak_main(_in_handle: u64) { oak::set_panic_hook(); let node = FrontendNode::new(); let dispatcher = OakAbiTestServiceDispatcher::new(node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); } @@ -1189,127 +1215,104 @@ impl FrontendNode { fn test_node_create_raw(&mut self) -> TestResult { let (_, in_channel, _) = channel_create_raw(); - let valid = "a_string"; - let non_utf8_name: Vec = vec![0xc3, 0x28]; let valid_label_bytes = Label::public_trusted().serialize(); - // This sequence of bytes should not deserialize as a [`oak_abi::proto::policy::Label`] - // protobuf. We make sure here that this continues to be the case by making sure that - // [`Label::deserialize`] fails to parse these bytes. - let invalid_label_bytes = vec![0, 88, 0]; - assert_eq!(None, Label::deserialize(&invalid_label_bytes)); - - unsafe { - expect_eq!( - OakStatus::Ok as u32, - oak_abi::node_create( - BACKEND_CONFIG_NAME.as_ptr(), - BACKEND_CONFIG_NAME.len(), - BACKEND_ENTRYPOINT_NAME.as_ptr(), - BACKEND_ENTRYPOINT_NAME.len(), - valid_label_bytes.as_ptr(), - valid_label_bytes.len(), - in_channel - ) - ); - - expect_eq!( - OakStatus::ErrInvalidArgs as u32, - oak_abi::node_create( - BACKEND_CONFIG_NAME.as_ptr(), - BACKEND_CONFIG_NAME.len(), - BACKEND_ENTRYPOINT_NAME.as_ptr(), - BACKEND_ENTRYPOINT_NAME.len(), - invalid_label_bytes.as_ptr(), - invalid_label_bytes.len(), - in_channel - ) - ); + // This sequence of bytes should not deserialize as a [`oak_abi::proto::policy::Label`] or + // [`oak_abi::proto::oak::application::NodeConfiguration`] protobuf. We make sure + // here that this continues to be the case by making sure that [`Label::decode`] and + // [`NodeConfiguration::decode`] fail to parse these bytes. + let invalid_proto_bytes = vec![0, 88, 0]; + assert_eq!(false, Label::decode(invalid_proto_bytes.as_ref()).is_ok()); + assert_eq!( + false, + NodeConfiguration::decode(invalid_proto_bytes.as_ref()).is_ok() + ); - expect_eq!( - OakStatus::ErrInvalidArgs as u32, + { + let mut config_bytes = Vec::new(); + oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME) + .encode(&mut config_bytes)?; + expect_eq!(OakStatus::Ok as u32, unsafe { oak_abi::node_create( - invalid_raw_offset() as *mut u8, - 1, - valid.as_ptr(), - valid.len(), + config_bytes.as_ptr(), + config_bytes.len(), valid_label_bytes.as_ptr(), valid_label_bytes.len(), - in_channel + in_channel, ) - ); + }); + } - expect_eq!( - OakStatus::ErrInvalidArgs as u32, + { + let mut config_bytes = Vec::new(); + oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME) + .encode(&mut config_bytes)?; + expect_eq!(OakStatus::ErrInvalidArgs as u32, unsafe { oak_abi::node_create( - non_utf8_name.as_ptr(), - non_utf8_name.len(), - valid.as_ptr(), - valid.len(), - valid_label_bytes.as_ptr(), - valid_label_bytes.len(), - in_channel + config_bytes.as_ptr(), + config_bytes.len(), + invalid_proto_bytes.as_ptr(), + invalid_proto_bytes.len(), + in_channel, ) - ); + }); + } - expect_eq!( - OakStatus::ErrInvalidArgs as u32, + { + expect_eq!(OakStatus::ErrInvalidArgs as u32, unsafe { oak_abi::node_create( - valid.as_ptr(), - valid.len(), invalid_raw_offset() as *mut u8, 1, valid_label_bytes.as_ptr(), valid_label_bytes.len(), - in_channel + in_channel, ) - ); + }); + } - expect_eq!( - OakStatus::ErrInvalidArgs as u32, + { + expect_eq!(OakStatus::ErrInvalidArgs as u32, unsafe { oak_abi::node_create( - valid.as_ptr(), - valid.len(), - non_utf8_name.as_ptr(), - non_utf8_name.len(), + invalid_proto_bytes.as_ptr(), + invalid_proto_bytes.len(), valid_label_bytes.as_ptr(), valid_label_bytes.len(), - in_channel + in_channel, ) - ); + }); } + Ok(()) } fn test_node_create(&mut self) -> TestResult { expect_eq!( Err(OakStatus::ErrInvalidArgs), oak::node_create( - "no-such-config", - BACKEND_ENTRYPOINT_NAME, + &oak::node_config::wasm("no_such_module", BACKEND_ENTRYPOINT_NAME), self.backend_in[0] ) ); expect_eq!( Err(OakStatus::ErrInvalidArgs), oak::node_create( - BACKEND_CONFIG_NAME, - "no-such-entrypoint", + &oak::node_config::wasm(BACKEND_MODULE_NAME, "no_such_entrypoint"), self.backend_in[0] ) ); expect_eq!( Err(OakStatus::ErrInvalidArgs), oak::node_create( - BACKEND_CONFIG_NAME, - "backend_fake_main", // exists but wrong signature + &oak::node_config::wasm( + BACKEND_MODULE_NAME, + "backend_fake_main" /* exists but wrong signature */ + ), self.backend_in[0] ) ); expect_eq!( Err(OakStatus::ErrBadHandle), oak::node_create( - BACKEND_CONFIG_NAME, - BACKEND_ENTRYPOINT_NAME, + &oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME), oak::ReadHandle { handle: oak::Handle::from_raw(oak_abi::INVALID_HANDLE) } @@ -1318,11 +1321,17 @@ impl FrontendNode { let (out_handle, in_handle) = oak::channel_create().unwrap(); expect_eq!( Ok(()), - oak::node_create(BACKEND_CONFIG_NAME, BACKEND_ENTRYPOINT_NAME, in_handle) + oak::node_create( + &oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME), + in_handle + ) ); expect_eq!( Ok(()), - oak::node_create(BACKEND_CONFIG_NAME, BACKEND_ENTRYPOINT_NAME, in_handle) + oak::node_create( + &oak::node_config::wasm(BACKEND_MODULE_NAME, BACKEND_ENTRYPOINT_NAME), + in_handle + ) ); expect_eq!(Ok(()), oak::channel_close(in_handle.handle)); @@ -1421,7 +1430,7 @@ impl FrontendNode { // Include some handles which will be ignored. let (logging_handle, read_handle) = oak::channel_create().expect("could not create channel"); - oak::node_create(LOG_CONFIG_NAME, "oak_main", read_handle).expect("could not create node"); + oak::node_create(&oak::node_config::log(), read_handle).expect("could not create node"); oak::channel_close(read_handle.handle).expect("could not close channel"); expect!(logging_handle.handle.is_valid()); @@ -1446,7 +1455,7 @@ impl FrontendNode { oak::channel_write( logging_handle, - &bytes[..], + bytes.as_ref(), &[in_handle.handle, out_handle.handle], ) .expect("could not write to channel"); diff --git a/examples/abitest/module_1/rust/src/lib.rs b/examples/abitest/module_1/rust/src/lib.rs index 61951ad4c82..43fb7f927cf 100644 --- a/examples/abitest/module_1/rust/src/lib.rs +++ b/examples/abitest/module_1/rust/src/lib.rs @@ -14,7 +14,7 @@ // limitations under the License. // -use abitest_common::{InternalMessage, LOG_CONFIG_NAME}; +use abitest_common::InternalMessage; use log::{error, info}; use std::collections::HashSet; @@ -42,8 +42,7 @@ pub extern "C" fn backend_oak_main(in_handle: u64) { } fn inner_main(in_handle: u64) -> Result<(), oak::OakStatus> { - oak::logger::init(log::Level::Debug, LOG_CONFIG_NAME) - .expect("Couldn't initialize logging node!"); + oak::logger::init(log::Level::Debug).expect("Couldn't initialize logging node!"); let in_channel = oak::ReadHandle { handle: oak::Handle::from_raw(in_handle), }; diff --git a/examples/abitest/tests/Cargo.toml b/examples/abitest/tests/Cargo.toml index eb87515828a..6b3b5dc2ddd 100644 --- a/examples/abitest/tests/Cargo.toml +++ b/examples/abitest/tests/Cargo.toml @@ -15,6 +15,7 @@ log = "*" oak = "=0.1.0" oak_abi = "=0.1.0" prost = "*" +tonic = { version = "*", features = ["tls"] } [dev-dependencies] assert_matches = "*" diff --git a/examples/abitest/tests/src/tests.rs b/examples/abitest/tests/src/tests.rs index 9a2eb06c01b..68e0c1c2a8a 100644 --- a/examples/abitest/tests/src/tests.rs +++ b/examples/abitest/tests/src/tests.rs @@ -19,13 +19,14 @@ use assert_matches::assert_matches; use log::{error, info}; use maplit::hashmap; use oak::grpc; +use oak_abi::proto::oak::application::ApplicationConfiguration; use std::collections::HashMap; +use tonic::transport::Certificate; // Constants for Node config names that should match those in the textproto // config held in ../../client/config.h. -const FRONTEND_CONFIG_NAME: &str = "frontend-config"; -const BACKEND_CONFIG_NAME: &str = "backend-config"; -const LOG_CONFIG_NAME: &str = "logging-config"; +const FRONTEND_MODULE_NAME: &str = "frontend_module"; +const BACKEND_MODULE_NAME: &str = "backend_module"; const FRONTEND_ENTRYPOINT_NAME: &str = "frontend_oak_main"; const FRONTEND_MANIFEST: &str = "../module_0/rust/Cargo.toml"; @@ -35,27 +36,34 @@ const FRONTEND_WASM_NAME: &str = "abitest_0_frontend.wasm"; const BACKEND_WASM_NAME: &str = "abitest_1_backend.wasm"; fn build_wasm() -> std::io::Result>> { - Ok(hashmap![ - FRONTEND_CONFIG_NAME.to_owned() => oak_tests::compile_rust_wasm(FRONTEND_MANIFEST, FRONTEND_WASM_NAME)?, - BACKEND_CONFIG_NAME.to_owned() => oak_tests::compile_rust_wasm(BACKEND_MANIFEST, BACKEND_WASM_NAME)?, - ]) + Ok(hashmap! { + FRONTEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(FRONTEND_MANIFEST, FRONTEND_WASM_NAME)?, + BACKEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(BACKEND_MANIFEST, BACKEND_WASM_NAME)?, + }) } #[test] fn test_abi() { simple_logger::init_by_env(); - let configuration = oak_runtime::application_configuration( - build_wasm().expect("failed to build wasm modules"), - LOG_CONFIG_NAME, - FRONTEND_CONFIG_NAME, - FRONTEND_ENTRYPOINT_NAME, - ); + let application_configuration = ApplicationConfiguration { + wasm_modules: build_wasm().expect("failed to build wasm modules"), + initial_node_configuration: Some(oak::node_config::wasm( + FRONTEND_MODULE_NAME, + FRONTEND_ENTRYPOINT_NAME, + )), + }; let (runtime, entry_channel) = oak_runtime::configure_and_run( - configuration, + application_configuration, oak_runtime::RuntimeConfiguration::default(), - oak_runtime::GrpcConfiguration::default(), + oak_runtime::GrpcConfiguration { + grpc_server_tls_identity: None, + // Some of the tests require a gRPC client, so we populate the required certificate with + // an invalid value here, even though it will still fail when instantiating the actual + // gRPC client. + grpc_client_root_tls_certificate: Some(Certificate::from_pem("invalid-cert")), + }, ) .expect("unable to configure runtime with test wasm"); @@ -77,6 +85,7 @@ fn test_abi() { runtime.stop_runtime(); let mut disabled = 0; + let mut success = true; for result in result.unwrap().results { if result.disabled { disabled += 1; @@ -89,10 +98,11 @@ fn test_abi() { ); if !result.success { error!("Failure details: {}", result.details); + success = false; } - assert_eq!(true, result.success); } if disabled > 0 { info!("YOU HAVE {} DISABLED TESTS", disabled); } + assert_eq!(true, success); } diff --git a/examples/aggregator/backend/Cargo.toml b/examples/aggregator/backend/Cargo.toml index 3dc823f67f0..933c9a842a8 100644 --- a/examples/aggregator/backend/Cargo.toml +++ b/examples/aggregator/backend/Cargo.toml @@ -13,7 +13,7 @@ log = "*" prost = "*" structopt = "*" tokio = { version = "*", features = ["fs", "macros", "sync", "stream"] } -tonic = { version = "=0.1.1", features = ["tls"] } +tonic = { version = "*", features = ["tls"] } [build-dependencies] tonic-build = "*" diff --git a/examples/aggregator/config/config.textproto b/examples/aggregator/config/config.textproto index 47ca8c9dcd9..e0b1d92d2a5 100644 --- a/examples/aggregator/config/config.textproto +++ b/examples/aggregator/config/config.textproto @@ -1,25 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "grpc-client" - grpc_client_config { - uri: "https://localhost:8888" - address: "127.0.0.1:8888" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/aggregator/module/rust/src/lib.rs b/examples/aggregator/module/rust/src/lib.rs index cfed4f92cf1..992a307f9bb 100644 --- a/examples/aggregator/module/rust/src/lib.rs +++ b/examples/aggregator/module/rust/src/lib.rs @@ -108,8 +108,11 @@ impl AggregatorNode { // }], // integrity_tags: vec![], // }; - match oak::grpc::client::Client::new_with_label("grpc-client", "", &Label::public_trusted()) - .map(AggregatorClient) + match oak::grpc::client::Client::new_with_label( + &oak::node_config::grpc_client("127.0.0.1:8888"), + &Label::public_trusted(), + ) + .map(AggregatorClient) { Some(grpc_client) => { let res = grpc_client.submit_sample(Sample { @@ -166,6 +169,6 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = AggregatorDispatcher::new(AggregatorNode::new()); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/examples/chat/config/config.textproto b/examples/chat/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/chat/config/config.textproto +++ b/examples/chat/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/chat/module/rust/Cargo.toml b/examples/chat/module/rust/Cargo.toml index e282e818c93..ec24c2d6a91 100644 --- a/examples/chat/module/rust/Cargo.toml +++ b/examples/chat/module/rust/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib"] bincode = "*" log = "*" oak = "=0.1.0" +oak_abi = "=0.1.0" prost = "*" rand_core = "*" rand = "*" diff --git a/examples/chat/module/rust/src/lib.rs b/examples/chat/module/rust/src/lib.rs index fc9c9eb42e3..aedfb5f56fe 100644 --- a/examples/chat/module/rust/src/lib.rs +++ b/examples/chat/module/rust/src/lib.rs @@ -47,7 +47,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = ChatDispatcher::new(Node::default()); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); @@ -59,7 +60,8 @@ struct Room { impl Room { fn new(admin_token: AdminToken) -> Self { let (wh, rh) = oak::channel_create().unwrap(); - oak::node_create("app", "backend_oak_main", rh).expect("could not create node"); + oak::node_create(&oak::node_config::wasm("app", "backend_oak_main"), rh) + .expect("could not create node"); oak::channel_close(rh.handle).expect("could not close channel"); Room { sender: oak::io::Sender::new(wh), diff --git a/examples/hello_world/config/config.textproto b/examples/hello_world/config/config.textproto index 412275ee8bb..e0b1d92d2a5 100644 --- a/examples/hello_world/config/config.textproto +++ b/examples/hello_world/config/config.textproto @@ -1,24 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "translator" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/hello_world/module/rust/src/lib.rs b/examples/hello_world/module/rust/src/lib.rs index 82c13981256..ddb736bdf28 100644 --- a/examples/hello_world/module/rust/src/lib.rs +++ b/examples/hello_world/module/rust/src/lib.rs @@ -27,7 +27,7 @@ use proto::{HelloRequest, HelloResponse, HelloWorld, HelloWorldDispatcher}; oak::entrypoint!(oak_main => |in_channel| { oak::logger::init_default(); let node = Node { - translator: grpc::client::Client::new("translator", "oak_main") + translator: grpc::client::Client::new(&oak::node_config::wasm("translator", "oak_main")) .map(translator_common::TranslatorClient), }; let dispatcher = HelloWorldDispatcher::new(node); @@ -37,11 +37,12 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let node = Node { - translator: grpc::client::Client::new("translator", "oak_main") + translator: grpc::client::Client::new(&oak::node_config::wasm("translator", "oak_main")) .map(translator_common::TranslatorClient), }; let dispatcher = HelloWorldDispatcher::new(node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/examples/machine_learning/config/config.textproto b/examples/machine_learning/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/machine_learning/config/config.textproto +++ b/examples/machine_learning/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/machine_learning/module/rust/src/lib.rs b/examples/machine_learning/module/rust/src/lib.rs index 69205e7cac8..45f0a28d9e3 100644 --- a/examples/machine_learning/module/rust/src/lib.rs +++ b/examples/machine_learning/module/rust/src/lib.rs @@ -165,7 +165,8 @@ oak::entrypoint!(grpc_oak_main => |_in_channel| { config: None, model: NaiveBayes::new(), }; - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(node, grpc_channel); }); diff --git a/examples/private_set_intersection/config/config.textproto b/examples/private_set_intersection/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/private_set_intersection/config/config.textproto +++ b/examples/private_set_intersection/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/private_set_intersection/module/rust/src/lib.rs b/examples/private_set_intersection/module/rust/src/lib.rs index f151eb64452..a579e398975 100644 --- a/examples/private_set_intersection/module/rust/src/lib.rs +++ b/examples/private_set_intersection/module/rust/src/lib.rs @@ -48,7 +48,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { let dispatcher = PrivateSetIntersectionDispatcher::new(Node::default()); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/examples/running_average/config/config.textproto b/examples/running_average/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/running_average/config/config.textproto +++ b/examples/running_average/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/running_average/module/rust/src/lib.rs b/examples/running_average/module/rust/src/lib.rs index ea5d03e3d66..f55c176774e 100644 --- a/examples/running_average/module/rust/src/lib.rs +++ b/examples/running_average/module/rust/src/lib.rs @@ -38,7 +38,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { let dispatcher = RunningAverageDispatcher::new(Node::default()); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/examples/rustfmt/config/config.textproto b/examples/rustfmt/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/rustfmt/config/config.textproto +++ b/examples/rustfmt/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/rustfmt/module/rust/src/lib.rs b/examples/rustfmt/module/rust/src/lib.rs index 3fec1c39bc1..997ae9f5aab 100644 --- a/examples/rustfmt/module/rust/src/lib.rs +++ b/examples/rustfmt/module/rust/src/lib.rs @@ -30,7 +30,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = FormatServiceDispatcher::new(Node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/examples/tensorflow/config/config.textproto b/examples/tensorflow/config/config.textproto index 045b8e054f3..191f0f11214 100644 --- a/examples/tensorflow/config/config.textproto +++ b/examples/tensorflow/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" diff --git a/examples/translator/config/config.textproto b/examples/translator/config/config.textproto index 40a0bd4bbd9..e0b1d92d2a5 100644 --- a/examples/translator/config/config.textproto +++ b/examples/translator/config/config.textproto @@ -1,18 +1,7 @@ -node_configs { - name: "app" - wasm_config { - module_bytes: "" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "grpc_oak_main" diff --git a/examples/translator/module/rust/src/lib.rs b/examples/translator/module/rust/src/lib.rs index bdad9b315a5..1919b5fc4f3 100644 --- a/examples/translator/module/rust/src/lib.rs +++ b/examples/translator/module/rust/src/lib.rs @@ -32,7 +32,8 @@ oak::entrypoint!(oak_main => |in_channel| { oak::entrypoint!(grpc_oak_main => |_in_channel| { oak::logger::init_default(); let dispatcher = TranslatorDispatcher::new(Node); - let grpc_channel = oak::grpc::server::init_default(); + let grpc_channel = + oak::grpc::server::init("[::]:8080").expect("could not create gRPC server pseudo-Node"); oak::run_event_loop(dispatcher, grpc_channel); }); diff --git a/oak/common/BUILD b/oak/common/BUILD index 334b3bda6e4..2640da397d0 100644 --- a/oak/common/BUILD +++ b/oak/common/BUILD @@ -54,24 +54,6 @@ cc_library( ], ) -cc_test( - name = "app_config_test", - srcs = ["app_config_test.cc"], - data = [":testdata"], - deps = [ - ":app_config", - "@com_google_absl//absl/memory", - "@gtest//:gtest_main", - ], -) - -filegroup( - name = "testdata", - srcs = glob([ - "testdata/*.textproto", - ]), -) - cc_library( name = "label", srcs = ["label.cc"], diff --git a/oak/common/app_config.cc b/oak/common/app_config.cc index 116ac70516a..97484394072 100644 --- a/oak/common/app_config.cc +++ b/oak/common/app_config.cc @@ -43,30 +43,11 @@ void WriteConfigToFile(const ApplicationConfiguration* config, const std::string } bool ValidApplicationConfig(const ApplicationConfiguration& config) { - // Check name uniqueness for NodeConfiguration. - std::set config_names; - std::set wasm_names; - for (const auto& node_config : config.node_configs()) { - if (config_names.count(node_config.name()) > 0) { - LOG(ERROR) << "duplicate node config name " << node_config.name(); - return false; - } - config_names.insert(node_config.name()); - if (node_config.has_wasm_config()) { - wasm_names.insert(node_config.name()); - } - } - - // Check name for the config of the initial node is present and is a Web - // Assembly variant. - if (wasm_names.count(config.initial_node_config_name()) == 0) { - LOG(ERROR) << "config of the initial node is not present in Wasm"; - return false; - } - if (config.initial_entrypoint_name().empty()) { - LOG(ERROR) << "missing entrypoint name"; + if (!config.has_initial_node_configuration()) { + LOG(ERROR) << "Missing initial node configuration"; return false; } + return true; } diff --git a/oak/common/app_config_serializer.cc b/oak/common/app_config_serializer.cc index 4c06b4fe597..5d28738fd13 100644 --- a/oak/common/app_config_serializer.cc +++ b/oak/common/app_config_serializer.cc @@ -29,10 +29,10 @@ ABSL_FLAG( std::string, textproto, "", - "Textproto file with an application configuration, where the `module_bytes` value is empty, " + "Textproto file with an application configuration, where the `wasm_modules` field is empty, " "(it will be overwritten by module bytes after serialization)"); ABSL_FLAG(std::vector, modules, std::vector{}, - "A comma-separated list of entries `module:path` with files containing compiled " + "A comma-separated list of entries `module_name:path` with files containing compiled " "WebAssembly modules to insert into the generated configuration"); ABSL_FLAG(std::string, output_file, "", "File to write an application configuration to"); @@ -41,51 +41,49 @@ int main(int argc, char* argv[]) { std::string textproto = absl::GetFlag(FLAGS_textproto); if (textproto.empty()) { LOG(FATAL) << "Textproto file is not specified"; + return 1; } std::vector modules = absl::GetFlag(FLAGS_modules); if (modules.empty()) { LOG(FATAL) << "Wasm modules are not specified"; + return 1; } std::string output_file = absl::GetFlag(FLAGS_output_file); if (output_file.empty()) { LOG(FATAL) << "Output file is not specified"; + return 1; } - // Parse module names. + // Load application configuration. + auto config = absl::make_unique(); + std::string textproto_string = oak::utils::read_file(textproto); + if (!google::protobuf::TextFormat::MergeFromString(textproto_string, config.get())) { + LOG(FATAL) << "Error parsing proto"; + return 1; + } + + // Parse module names and add Wasm module bytes to the application configuration. std::map module_map; for (const std::string& module : absl::GetFlag(FLAGS_modules)) { std::vector module_info = absl::StrSplit(module, ':'); if (module_info.size() != 2) { LOG(FATAL) << "Incorrect module specification: " << module; + return 1; } - module_map.emplace(module_info.front(), module_info.back()); - } - - // Load application configuration. - auto config = absl::make_unique(); - std::string textproto_string = oak::utils::read_file(textproto); - google::protobuf::TextFormat::MergeFromString(textproto_string, config.get()); - - // Add Wasm module bytes to the application configuration. - for (auto& node_config : *config->mutable_node_configs()) { - if (node_config.has_wasm_config()) { - std::string module_name = node_config.name(); - auto it = module_map.find(module_name); - if (it != module_map.end()) { - std::string module_bytes = oak::utils::read_file(it->second); - if (module_bytes.empty()) { - LOG(FATAL) << "Empty Wasm module:" << module_name; - } - node_config.mutable_wasm_config()->set_module_bytes(module_bytes); - } else { - LOG(FATAL) << "Module path for " << module_name << " is not specified"; - } + std::string module_name = module_info[0]; + std::string module_path = module_info[1]; + std::string module_bytes = oak::utils::read_file(module_path); + if (module_bytes.empty()) { + LOG(FATAL) << "Empty Wasm module:" << module_name; + return 1; } + (*config->mutable_wasm_modules())[module_name] = std::move(module_bytes); } // Check application configuration validity. if (!oak::ValidApplicationConfig(*config)) { LOG(FATAL) << "Application config is not valid"; + return 1; } oak::WriteConfigToFile(config.get(), output_file); diff --git a/oak/common/app_config_test.cc b/oak/common/app_config_test.cc deleted file mode 100644 index 6b1b5bd79fa..00000000000 --- a/oak/common/app_config_test.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2019 The Project Oak Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "oak/common/app_config.h" - -#include -#include - -#include "absl/memory/memory.h" -#include "google/protobuf/text_format.h" -#include "gtest/gtest.h" - -using oak::application::ApplicationConfiguration; - -namespace oak { - -namespace { - -// Fixture class for testing `ApplicationConfiguration` correctness. -class ApplicationConfigurationTest : public ::testing::Test { - protected: - void TearDown() override { - // Clean up temporary files. - std::remove(kTmpFilename.c_str()); - } - - std::unique_ptr ConfigFrom(const std::string& filename) { - std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary); - EXPECT_TRUE(ifs.is_open()) << "failed to open " << filename; - std::stringstream ss; - ss << ifs.rdbuf(); - ifs.close(); - - auto config = absl::make_unique(); - google::protobuf::TextFormat::MergeFromString(ss.str(), config.get()); - return config; - } - - const std::string kTmpFilename = "oak/common/testdata/tmp.bin"; -}; - -TEST_F(ApplicationConfigurationTest, ReadWriteFile) { - auto want = ConfigFrom("oak/common/testdata/default.textproto"); - - std::ifstream existing_file(kTmpFilename, std::ifstream::in); - ASSERT_FALSE(existing_file.is_open()); - WriteConfigToFile(want.get(), kTmpFilename); - - std::unique_ptr got = ReadConfigFromFile(kTmpFilename); - ASSERT_EQ(want->DebugString(), got->DebugString()); - ASSERT_EQ(true, ValidApplicationConfig(*got)); -} - -TEST_F(ApplicationConfigurationTest, Valid) { - auto config = ConfigFrom("oak/common/testdata/default.textproto"); - ASSERT_EQ(true, ValidApplicationConfig(*config)); -} - -TEST_F(ApplicationConfigurationTest, DuplicateConfigName) { - auto config = ConfigFrom("oak/common/testdata/dup_config_name.textproto"); - ASSERT_EQ(false, ValidApplicationConfig(*config)); -} - -TEST_F(ApplicationConfigurationTest, MultipleLogNodes) { - // Two log configs are OK. - auto config = ConfigFrom("oak/common/testdata/two_log.textproto"); - ASSERT_EQ(true, ValidApplicationConfig(*config)); -} - -TEST_F(ApplicationConfigurationTest, NoWasmCode) { - auto config = ConfigFrom("oak/common/testdata/missing_wasm.textproto"); - ASSERT_EQ(false, ValidApplicationConfig(*config)); -} - -TEST_F(ApplicationConfigurationTest, DuplicateWasmName) { - auto config = ConfigFrom("oak/common/testdata/dup_wasm.textproto"); - ASSERT_EQ(false, ValidApplicationConfig(*config)); -} - -TEST_F(ApplicationConfigurationTest, EmptyWasmEntrypoint) { - auto config = ConfigFrom("oak/common/testdata/no_wasm_main.textproto"); - ASSERT_EQ(false, ValidApplicationConfig(*config)); -} - -} // namespace - -} // namespace oak diff --git a/oak/common/testdata/barenode.textproto b/oak/common/testdata/barenode.textproto deleted file mode 100644 index 5ab855ecbd5..00000000000 --- a/oak/common/testdata/barenode.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/default.textproto b/oak/common/testdata/default.textproto deleted file mode 100644 index dfc70aa5299..00000000000 --- a/oak/common/testdata/default.textproto +++ /dev/null @@ -1,22 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "log" - log_config {} -} -node_configs { - name: "storage" - storage_config { - address: "localhost:8888" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/dup_config_name.textproto b/oak/common/testdata/dup_config_name.textproto deleted file mode 100644 index 31c669b55e2..00000000000 --- a/oak/common/testdata/dup_config_name.textproto +++ /dev/null @@ -1,16 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "app" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/dup_wasm.textproto b/oak/common/testdata/dup_wasm.textproto deleted file mode 100644 index d0e791fa2bc..00000000000 --- a/oak/common/testdata/dup_wasm.textproto +++ /dev/null @@ -1,18 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/grpcclient.textproto b/oak/common/testdata/grpcclient.textproto deleted file mode 100644 index 45f820005d4..00000000000 --- a/oak/common/testdata/grpcclient.textproto +++ /dev/null @@ -1,18 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "grpc-client" - grpc_client_config { - address: "localhost:9999" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/grpcport.textproto b/oak/common/testdata/grpcport.textproto deleted file mode 100644 index 1007582a3dc..00000000000 --- a/oak/common/testdata/grpcport.textproto +++ /dev/null @@ -1,12 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8888 diff --git a/oak/common/testdata/lognode.textproto b/oak/common/testdata/lognode.textproto deleted file mode 100644 index c9bd87f7373..00000000000 --- a/oak/common/testdata/lognode.textproto +++ /dev/null @@ -1,16 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "log" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/missing_wasm.textproto b/oak/common/testdata/missing_wasm.textproto deleted file mode 100644 index 4b3a5e1799f..00000000000 --- a/oak/common/testdata/missing_wasm.textproto +++ /dev/null @@ -1,16 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "log" - log_config {} -} -node_configs { - name: "storage" - storage_config { - address: "localhost:8888" - } -} -initial_node_config_name: "log" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/no_wasm_main.textproto b/oak/common/testdata/no_wasm_main.textproto deleted file mode 100644 index ef6b14ecafe..00000000000 --- a/oak/common/testdata/no_wasm_main.textproto +++ /dev/null @@ -1,21 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "log" - log_config {} -} -node_configs { - name: "storage" - storage_config { - address: "localhost:8888" - } -} -initial_node_config_name: "app" -grpc_port: 8080 diff --git a/oak/common/testdata/storagenode.textproto b/oak/common/testdata/storagenode.textproto deleted file mode 100644 index 5330c88e434..00000000000 --- a/oak/common/testdata/storagenode.textproto +++ /dev/null @@ -1,18 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "storage" - storage_config { - address: "localhost:8888" - } -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/common/testdata/two_log.textproto b/oak/common/testdata/two_log.textproto deleted file mode 100644 index 3db07a21358..00000000000 --- a/oak/common/testdata/two_log.textproto +++ /dev/null @@ -1,20 +0,0 @@ -# proto-file: oak/proto/manager.proto -# proto-message: oak.ApplicationConfiguration - -node_configs { - name: "app" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "log" - log_config {} -} -node_configs { - name: "log2" - log_config {} -} -initial_node_config_name: "app" -initial_entrypoint_name: "oak_main" -grpc_port: 8080 diff --git a/oak/proto/application.proto b/oak/proto/application.proto index d4f8e1472fa..498c581d645 100644 --- a/oak/proto/application.proto +++ b/oak/proto/application.proto @@ -22,25 +22,33 @@ package oak.application; // // An Oak Application is built from a collection of interconnected Nodes, // each of which is running the code described by an entry in this -// configuration. These Nodes are created dynamically at runtime, with +// configuration. These Nodes are created dynamically at runtime, with // the exception of the specified initial Node (which is created by the // Oak runtime). message ApplicationConfiguration { - // Collection of available Node configurations, indexed by name (which must be - // unique across the collection). Each Node in the application will run under - // a configuration that is identified by an entry in this collection. - repeated NodeConfiguration node_configs = 1; - // Indication of what configuration the initial Node should run. Must identify a - // NodeConfiguration entry that holds a WebAssemblyConfiguration object. - string initial_node_config_name = 2; - // The name of an exported Web Assembly function in the initial Node to - // be used as the Node's main entrypoint. - string initial_entrypoint_name = 3; + // Map from Wasm module names to their bytecode representation. + // + // See https://webassembly.org/docs/binary-encoding/ . + // + // Any developer-authored code running as part of an Oak Application must appear in this map. + // + // Entries in this map may be referenced by instances of WebAssemblyConfiguration + // objects. + map wasm_modules = 1; + + // Indication of what configuration the initial Node should run. + // + // Usually a NodeConfiguration entry that holds a WebAssemblyConfiguration object. + NodeConfiguration initial_node_configuration = 2; } // NodeConfiguration indicates the configuration of a created Node. message NodeConfiguration { + // Display name of the newly created node instance, for debugging purposes. + // + // Does not need to be unique. string name = 1; + oneof config_type { WebAssemblyConfiguration wasm_config = 2; LogConfiguration log_config = 3; @@ -53,11 +61,11 @@ message NodeConfiguration { // WebAssemblyConfiguration describes the configuration of a Web Assembly based Node. message WebAssemblyConfiguration { - // The compiled code of the Oak Node, in WebAssembly binary format. - // See https://webassembly.org/docs/binary-encoding/ . - // TODO(#804): Replace this with just a hash of the bytecode to instantiate, and - // pass the actual bytecode to the Oak Manager in some other way. - bytes module_bytes = 1; + // The name of one of the entries in the `ApplicationConfiguration.wasm_modules` field. + string wasm_module_name = 1; + + // The name of an exported WebAssembly function to invoke as the Node entry point. + string wasm_entrypoint_name = 2; } // LogConfiguration describes the configuration of a logging pseudo-Node (which diff --git a/oak/server/rust/oak_abi/build.rs b/oak/server/rust/oak_abi/build.rs index b9b8200d90b..408c05290b8 100644 --- a/oak/server/rust/oak_abi/build.rs +++ b/oak/server/rust/oak_abi/build.rs @@ -17,6 +17,7 @@ fn main() { oak_utils::compile_protos( &[ + "../../../../oak/proto/application.proto", "../../../../oak/proto/grpc_encap.proto", "../../../../oak/proto/label.proto", "../../../../oak/proto/log.proto", diff --git a/oak/server/rust/oak_abi/src/lib.rs b/oak/server/rust/oak_abi/src/lib.rs index c7757265f28..6baaf5e8b08 100644 --- a/oak/server/rust/oak_abi/src/lib.rs +++ b/oak/server/rust/oak_abi/src/lib.rs @@ -127,22 +127,17 @@ extern "C" { label_len: usize, ) -> u32; - /// Close a channel. - /// - /// Close the channel identified by `handle`. + /// Closes the channel identified by `handle`. /// /// Returns the status of the operation, as an [`OakStatus`] value. /// /// [`OakStatus`]: crate::OakStatus pub fn channel_close(handle: u64) -> u32; - /// Create a new Node instance running code identified by configuration name and entrypoint; the - /// entrypoint is only used when creating a WebAssembly Node; it is ignored when creating a - /// pseudo-Node. + /// Creates a new Node instance running code identified by a serialized [`NodeConfiguration`]. /// - /// The configuration name is provided in the memory area given by `config_buf` and - /// `config_len`; the entrypoint name is provided in the memory area given by `entrypoint_buf` - /// and `entrypoint_len`. + /// The serialized configuration object is provided in the memory area given by `config_buf` and + /// `config_len`. /// /// The label to assign to the newly created Node is provided in the memory area given by /// `label_buf` and `label_len`. @@ -150,11 +145,10 @@ extern "C" { /// Returns the status of the operation, as an [`OakStatus`] value. /// /// [`OakStatus`]: crate::OakStatus + /// [`NodeConfiguration`]: crate::proto::oak::application::NodeConfiguration pub fn node_create( config_buf: *const u8, config_len: usize, - entrypoint_buf: *const u8, - entrypoint_len: usize, label_buf: *const u8, label_len: usize, handle: u64, diff --git a/oak/server/rust/oak_abi/src/proto/mod.rs b/oak/server/rust/oak_abi/src/proto/mod.rs index cbaa685a38a..fbb36ca14a6 100644 --- a/oak/server/rust/oak_abi/src/proto/mod.rs +++ b/oak/server/rust/oak_abi/src/proto/mod.rs @@ -23,6 +23,10 @@ pub mod google { pub mod oak { include!(concat!(env!("OUT_DIR"), "/oak_abi.rs")); + pub mod application { + include!(concat!(env!("OUT_DIR"), "/oak.application.rs")); + } + pub mod label { include!(concat!(env!("OUT_DIR"), "/oak.label.rs")); } diff --git a/oak/server/rust/oak_glue/src/lib.rs b/oak/server/rust/oak_glue/src/lib.rs index 8fab9645665..2478e9e1d4f 100644 --- a/oak/server/rust/oak_glue/src/lib.rs +++ b/oak/server/rust/oak_glue/src/lib.rs @@ -17,10 +17,10 @@ use byteorder::{ReadBytesExt, WriteBytesExt}; use lazy_static::lazy_static; use log::{debug, error, info, warn}; -use oak_abi::OakStatus; +use oak_abi::{proto::oak::application::ApplicationConfiguration, OakStatus}; use oak_runtime::{ - proto::oak::application::ApplicationConfiguration, runtime::RuntimeProxy, GrpcConfiguration, - NodeId, + runtime::{NodeId, RuntimeProxy}, + GrpcConfiguration, }; use prost::Message; use std::{convert::TryInto, io::Cursor, sync::RwLock}; @@ -39,14 +39,14 @@ type NodeFactory = extern "C" fn(data: usize, name: *const u8, name_len: u32, node_id: u64, handle: u64) -> (); struct Glue { - runtime: oak_runtime::RuntimeProxy, + runtime: oak_runtime::runtime::RuntimeProxy, factory: Option, factory_data: usize, } impl Glue { fn new( - runtime: oak_runtime::RuntimeProxy, + runtime: oak_runtime::runtime::RuntimeProxy, factory: Option, factory_data: usize, ) -> Self { @@ -106,8 +106,8 @@ pub unsafe extern "C" fn glue_start( introspect_port: Some(1909), }; info!( - "start runtime with initial config {}.{} {:?}", - app_config.initial_node_config_name, app_config.initial_entrypoint_name, runtime_config + "start runtime with application config {:?}, runtime config {:?}", + app_config, runtime_config ); // Register callback for creating C++ pseudo-Nodes. diff --git a/oak/server/rust/oak_loader/Cargo.toml b/oak/server/rust/oak_loader/Cargo.toml index 2ea666c0e46..b69fd775d43 100644 --- a/oak/server/rust/oak_loader/Cargo.toml +++ b/oak/server/rust/oak_loader/Cargo.toml @@ -16,6 +16,7 @@ default = ["oak_debug"] anyhow = "*" log = "*" oak_runtime = "=0.1.0" +oak_abi = "=0.1.0" prost = "*" rustls = "*" signal-hook = "*" diff --git a/oak/server/rust/oak_loader/src/main.rs b/oak/server/rust/oak_loader/src/main.rs index 35913c68066..e5665c2ec6a 100644 --- a/oak/server/rust/oak_loader/src/main.rs +++ b/oak/server/rust/oak_loader/src/main.rs @@ -27,10 +27,8 @@ use anyhow::anyhow; use core::str::FromStr; use log::{debug, info}; -use oak_runtime::{ - configure_and_run, - proto::oak::application::{ApplicationConfiguration, ConfigMap}, -}; +use oak_abi::proto::oak::application::{ApplicationConfiguration, ConfigMap}; +use oak_runtime::config::configure_and_run; use prost::Message; use std::{ collections::HashMap, diff --git a/oak/server/rust/oak_runtime/build.rs b/oak/server/rust/oak_runtime/build.rs deleted file mode 100644 index 74f19b44e86..00000000000 --- a/oak/server/rust/oak_runtime/build.rs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright 2019 The Project Oak Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -fn main() { - oak_utils::compile_protos( - &["../../../../oak/proto/application.proto"], - &["../../../../oak/proto"], - ); -} diff --git a/oak/server/rust/oak_runtime/src/config.rs b/oak/server/rust/oak_runtime/src/config.rs index d701c8a4044..807bbd32aaf 100644 --- a/oak/server/rust/oak_runtime/src/config.rs +++ b/oak/server/rust/oak_runtime/src/config.rs @@ -14,136 +14,21 @@ // limitations under the License. // -use crate::{ - node, - node::{check_uri, load_wasm}, - proto::oak::application::{ - node_configuration::ConfigType, ApplicationConfiguration, GrpcClientConfiguration, - GrpcServerConfiguration, LogConfiguration, NodeConfiguration, RoughtimeClientConfiguration, - StorageProxyConfiguration, WebAssemblyConfiguration, - }, - runtime, RuntimeProxy, -}; -use itertools::Itertools; -use log::error; -use oak_abi::OakStatus; -use std::collections::HashMap; +use crate::RuntimeProxy; +use oak_abi::{proto::oak::application::ApplicationConfiguration, OakStatus}; -/// Create an application configuration. +/// Configures a [`RuntimeProxy`] from the given protobuf [`ApplicationConfiguration`] and begins +/// execution. /// -/// - module_name_wasm: collection of Wasm bytes indexed by config name. -/// - logger_name: Node name to use for a logger configuration; if empty no logger will be included. -/// - initial_node: Initial Node to run on launch. -/// - entrypoint: Entrypoint in the initial Node to run on launch. -pub fn application_configuration( - module_name_wasm: HashMap, S>, - logger_name: &str, - initial_node: &str, - entrypoint: &str, -) -> ApplicationConfiguration { - let mut nodes: Vec = module_name_wasm - .into_iter() - .sorted() - .map(|(name, wasm)| NodeConfiguration { - name, - config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration { - module_bytes: wasm, - })), - }) - .collect(); - - if !logger_name.is_empty() { - nodes.push(NodeConfiguration { - name: logger_name.to_string(), - config_type: Some(ConfigType::LogConfig(LogConfiguration {})), - }); - } - - ApplicationConfiguration { - node_configs: nodes, - initial_node_config_name: initial_node.into(), - initial_entrypoint_name: entrypoint.into(), - } -} - -/// Load a `runtime::Configuration` from a protobuf `ApplicationConfiguration`. -/// This can fail if an unsupported Node is passed, or if a Node was unable to be initialized with -/// the given configuration. -pub fn from_protobuf( - app_config: ApplicationConfiguration, -) -> Result { - let mut config = runtime::Configuration { - nodes: HashMap::new(), - entry_module: app_config.initial_node_config_name.clone(), - entrypoint: app_config.initial_entrypoint_name.clone(), - }; - - for node in app_config.node_configs { - config.nodes.insert( - node.name.clone(), - match &node.config_type { - None => { - error!("Node config {} with no type", node.name); - return Err(OakStatus::ErrInvalidArgs); - } - Some(ConfigType::LogConfig(_)) => node::Configuration::LogNode, - Some(ConfigType::GrpcServerConfig(GrpcServerConfiguration { address })) => { - node::Configuration::GrpcServerNode { - address: address.parse().map_err(|error| { - error!("Incorrect gRPC server address: {:?}", error); - OakStatus::ErrInvalidArgs - })?, - } - } - Some(ConfigType::GrpcClientConfig(GrpcClientConfiguration { uri, address })) => { - node::Configuration::GrpcClientNode { - uri: uri - .parse() - .map_err(|error| { - error!("Error parsing URI {}: {:?}", uri, error); - OakStatus::ErrInvalidArgs - }) - .and_then(|uri| match check_uri(&uri) { - Ok(_) => Ok(uri), - Err(error) => { - error!("Incorrect URI {}: {:?}", uri, error); - Err(OakStatus::ErrInvalidArgs) - } - })?, - address: address.to_string(), - } - } - Some(ConfigType::WasmConfig(WebAssemblyConfiguration { module_bytes, .. })) => { - load_wasm(&module_bytes).map_err(|error| { - error!("Error loading Wasm module: {}", error); - OakStatus::ErrInvalidArgs - })? - } - Some(ConfigType::StorageConfig(StorageProxyConfiguration { .. })) => { - node::Configuration::StorageNode - } - Some(ConfigType::RoughtimeClientConfig(RoughtimeClientConfiguration { - .. - })) => node::Configuration::RoughtimeClientNode, - }, - ); - } - - Ok(config) -} - -/// Configure a [`RuntimeProxy`] from the given protobuf [`ApplicationConfiguration`] and begin -/// execution. This returns a [`RuntimeProxy`] for an initial implicit Node, and a writeable -/// [`oak_abi::Handle`] to send messages into the Runtime. Creating a new channel and -/// passing the write [`oak_abi::Handle`] into the runtime will enable messages to be read -/// back out from the [`RuntimeProxy`]. +/// Returns a [`RuntimeProxy`] for an initial implicit Node, and a writeable [`oak_abi::Handle`] to +/// send messages into the Runtime. Creating a new channel and passing the write [`oak_abi::Handle`] +/// into the runtime will enable messages to be read back out from the [`RuntimeProxy`]. pub fn configure_and_run( application_configuration: ApplicationConfiguration, runtime_configuration: crate::RuntimeConfiguration, grpc_configuration: crate::GrpcConfiguration, ) -> Result<(RuntimeProxy, oak_abi::Handle), OakStatus> { - let configuration = from_protobuf(application_configuration)?; - let proxy = RuntimeProxy::create_runtime(configuration, grpc_configuration); + let proxy = RuntimeProxy::create_runtime(application_configuration, grpc_configuration); let handle = proxy.start_runtime(runtime_configuration)?; Ok((proxy, handle)) } diff --git a/oak/server/rust/oak_runtime/src/lib.rs b/oak/server/rust/oak_runtime/src/lib.rs index bef2e9373f0..a9067050e4a 100644 --- a/oak/server/rust/oak_runtime/src/lib.rs +++ b/oak/server/rust/oak_runtime/src/lib.rs @@ -23,8 +23,6 @@ //! be enabled in development, as it destroys the privacy guarantees of the //! platform by providing easy channels for the exfiltration of private data. -pub mod proto; - pub mod config; pub mod io; pub mod message; @@ -34,7 +32,7 @@ pub mod runtime; use tonic::transport::{Certificate, Identity}; -pub use config::{application_configuration, configure_and_run}; +pub use config::configure_and_run; pub use message::NodeMessage; pub use runtime::{NodeId, RuntimeProxy}; diff --git a/oak/server/rust/oak_runtime/src/node/grpc/client.rs b/oak/server/rust/oak_runtime/src/node/grpc/client.rs index 60224bac813..2c9e33e23ea 100644 --- a/oak/server/rust/oak_runtime/src/node/grpc/client.rs +++ b/oak/server/rust/oak_runtime/src/node/grpc/client.rs @@ -18,13 +18,16 @@ use crate::{ io::Receiver, node::{ grpc::{codec::VecCodec, from_tonic_status, invocation::Invocation}, - Node, + ConfigurationError, Node, }, runtime::RuntimeProxy, }; use log::{debug, error, info}; use oak_abi::{ - proto::oak::encap::{GrpcRequest, GrpcResponse}, + proto::oak::{ + application::GrpcClientConfiguration, + encap::{GrpcRequest, GrpcResponse}, + }, Handle, OakStatus, }; use tokio::sync::oneshot; @@ -38,19 +41,30 @@ pub struct GrpcClientNode { handler: GrpcRequestHandler, } +/// Checks if URI contains the "Host" element. +fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> { + uri.authority() + .filter(|authority| !authority.host().is_empty()) + .map(|_| ()) + .ok_or(ConfigurationError::NoHostElement) +} + impl GrpcClientNode { /// Creates a new [`GrpcClientNode`] instance, but does not start it. - /// - /// Arguments: - /// * `node_name` - Pseudo-Node name. - /// * `uri` - The URI component of a gRPC server endpoint. Must contain the "Host" element. - /// * `root_tls_certificate` - Loaded PEM encoded X.509 TLS root certificate file used to - /// authenticate an external gRPC service. - pub fn new(node_name: &str, uri: Uri, root_tls_certificate: Certificate) -> Self { - Self { + pub fn new( + node_name: &str, + config: GrpcClientConfiguration, + root_tls_certificate: Certificate, + ) -> Result { + let uri = config.uri.parse().map_err(|error| { + error!("Error parsing URI {}: {:?}", config.uri, error); + ConfigurationError::IncorrectURI + })?; + check_uri(&uri)?; + Ok(Self { node_name: node_name.to_string(), handler: GrpcRequestHandler::new(uri, root_tls_certificate), - } + }) } /// Main loop that handles gRPC invocations from the `handle`, sends gRPC requests to an diff --git a/oak/server/rust/oak_runtime/src/node/grpc/server.rs b/oak/server/rust/oak_runtime/src/node/grpc/server.rs index 7a05f86c5fb..b4ec3a42356 100644 --- a/oak/server/rust/oak_runtime/src/node/grpc/server.rs +++ b/oak/server/rust/oak_runtime/src/node/grpc/server.rs @@ -17,7 +17,7 @@ use crate::{ node::{ grpc::{codec::VecCodec, to_tonic_status}, - Node, + ConfigurationError, Node, }, runtime::RuntimeProxy, }; @@ -25,7 +25,10 @@ use hyper::service::Service; use log::{debug, error, info}; use oak_abi::{ label::Label, - proto::oak::encap::{GrpcRequest, GrpcResponse}, + proto::oak::{ + application::GrpcServerConfiguration, + encap::{GrpcRequest, GrpcResponse}, + }, ChannelReadStatus, OakStatus, }; use prost::Message; @@ -50,14 +53,29 @@ pub struct GrpcServerNode { tls_identity: Identity, } +/// Checks if port is greater than 1023. +fn check_port(address: &SocketAddr) -> Result<(), ConfigurationError> { + if address.port() > 1023 { + Ok(()) + } else { + Err(ConfigurationError::IncorrectPort) + } +} + impl GrpcServerNode { /// Creates a new [`GrpcServerNode`] instance, but does not start it. - pub fn new(node_name: &str, address: SocketAddr, tls_identity: Identity) -> Self { - Self { + pub fn new( + node_name: &str, + config: GrpcServerConfiguration, + tls_identity: Identity, + ) -> Result { + let address = config.address.parse()?; + check_port(&address)?; + Ok(Self { node_name: node_name.to_string(), address, tls_identity, - } + }) } /// Reads an [`oak_abi::Handle`] from a channel specified by `handle`. diff --git a/oak/server/rust/oak_runtime/src/node/mod.rs b/oak/server/rust/oak_runtime/src/node/mod.rs index dc531fc4458..48a686ea9a9 100644 --- a/oak/server/rust/oak_runtime/src/node/mod.rs +++ b/oak/server/rust/oak_runtime/src/node/mod.rs @@ -15,14 +15,11 @@ // use crate::{runtime::RuntimeProxy, GrpcConfiguration}; -use log::debug; -use std::{ - net::{AddrParseError, SocketAddr}, - string::String, - sync::Arc, +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, ApplicationConfiguration, LogConfiguration, NodeConfiguration, }; +use std::net::AddrParseError; use tokio::sync::oneshot; -use tonic::transport::Uri; pub mod external; mod grpc; @@ -32,8 +29,8 @@ mod storage; mod wasm; /// Trait encapsulating execution of a Node or pseudo-Node. -pub trait Node { - /// Execute the Node, using the provided `Runtime` reference and initial handle. The method +pub trait Node: Send { + /// Execute the Node, using the provided `Runtime` reference and initial handle. The method /// should continue execution until the Node terminates. /// /// `notify_receiver` receives a notification from the Runtime upon termination. This @@ -46,52 +43,16 @@ pub trait Node { ); } -/// A [`Configuration`] corresponds to a [`NodeConfiguration`] protobuf message, with parsed -/// and validated configuration information. -/// -/// [`NodeConfiguration`]: crate::proto::oak::application::NodeConfiguration -pub enum Configuration { - LogNode, - - /// The configuration for a gRPC server pseudo-Node that contains an `address` to listen on and - /// a TLS identity that consists of a private RSA key and an X.509 TLS certificate. - GrpcServerNode { - address: SocketAddr, - }, - - /// The configuration for a gRPC server pseudo-Node that contains a URI and an X.509 root TLS - /// certificate. - GrpcClientNode { - uri: Uri, - address: String, - }, - - /// The configuration for a Wasm Node. - // It would be better to store a list of exported methods and copyable Wasm interpreter - // instance, but wasmi doesn't allow this. We make do with having a copyable - // `Arc` that we pass to new Nodes, and clone to to spawn new - // `wasmi::ModuleInstances`. - WasmNode { - module: Arc, - }, - - /// The configuration for a storage Node. - StorageNode, - - /// The configuration for a Roughtime client Node. - RoughtimeClientNode, - - /// The configuration for an externally provided pseudo-Node. - External, -} - -/// An enumeration for errors occuring when building [`Configuration`] from protobuf types. +/// A enumeration for errors occuring when creating a new [`Node`] instance. #[derive(Debug)] pub enum ConfigurationError { AddressParsingError(AddrParseError), IncorrectPort, + IncorrectURI, NoHostElement, CertificateParsingError, + IncorrectWebAssemblyModuleName, + InvalidNodeConfiguration, WasmiModuleInializationError(wasmi::Error), } @@ -108,10 +69,15 @@ impl std::fmt::Display for ConfigurationError { write!(f, "Failed to parse an address: {}", e) } ConfigurationError::IncorrectPort => write!(f, "Incorrect port (must be > 1023)"), + ConfigurationError::IncorrectURI => write!(f, "Incorrect URI"), ConfigurationError::NoHostElement => write!(f, "URI doesn't contain the Host element"), ConfigurationError::CertificateParsingError => { write!(f, "Error parsing PEM encoded TLS certificate") } + ConfigurationError::IncorrectWebAssemblyModuleName => { + write!(f, "Incorrect WebAssembly module name") + } + ConfigurationError::InvalidNodeConfiguration => write!(f, "Invalid NodeConfiguration"), ConfigurationError::WasmiModuleInializationError(e) => { write!(f, "Failed to initialize wasmi::Module: {}", e) } @@ -119,101 +85,49 @@ impl std::fmt::Display for ConfigurationError { } } -/// Loads a Wasm module into a Node configuration, returning an error if `wasmi` failed to load the -/// module. -pub fn load_wasm(wasm_bytes: &[u8]) -> Result { - let module = wasmi::Module::from_buffer(wasm_bytes) - .map_err(ConfigurationError::WasmiModuleInializationError)?; - Ok(Configuration::WasmNode { - module: Arc::new(module), - }) -} - -/// Checks if port is greater than 1023. -pub fn check_port(address: &SocketAddr) -> Result<(), ConfigurationError> { - if address.port() > 1023 { - Ok(()) - } else { - Err(ConfigurationError::IncorrectPort) - } -} - -/// Checks if URI contains the "Host" element. -pub fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> { - uri.authority() - .filter(|authority| !authority.host().is_empty()) - .map(|_| ()) - .ok_or(ConfigurationError::NoHostElement) -} - -impl Configuration { - /// Creates a new Node instance corresponding to the [`Configuration`]. - /// - /// On success returns a boxed [`Node`] that can be run with [`Node::run`]. - pub fn create_node( - &self, - node_name: &str, // Used for pretty debugging - config_name: &str, - entrypoint: String, - grpc_configuration: &GrpcConfiguration, - ) -> Option> { - debug!( - "create_node('{}': '{}'.'{}')", - node_name, config_name, entrypoint - ); - match self { - Configuration::LogNode => Some(Box::new(logger::LogNode::new(node_name))), - Configuration::GrpcServerNode { address } => { - Some(Box::new(grpc::server::GrpcServerNode::new( - node_name, - *address, - grpc_configuration - .grpc_server_tls_identity - .as_ref() - .expect("no gRPC server TLS identity provided") - .clone(), - ))) - } - Configuration::GrpcClientNode { uri, address: _ } => { - Some(Box::new(grpc::client::GrpcClientNode::new( - node_name, - uri.clone(), - grpc_configuration - .grpc_client_root_tls_certificate - .as_ref() - .expect("no gRPC client root TLS certificate provided") - .clone(), - ))) - } - Configuration::WasmNode { module } => { - match wasm::WasmNode::new(node_name, module.clone(), entrypoint) { - Some(node) => Some(Box::new(node)), - None => None, - } - } - - Configuration::StorageNode => Some(Box::new(storage::StorageNode::new(node_name))), - Configuration::RoughtimeClientNode => { - Some(Box::new(roughtime::RoughtimeClientNode::new(node_name))) - } - - Configuration::External => { - Some(Box::new(external::PseudoNode::new(node_name, config_name))) - } +pub fn create_node( + application_configuration: &ApplicationConfiguration, + node_configuration: &NodeConfiguration, + grpc_configuration: &GrpcConfiguration, +) -> Result, ConfigurationError> { + let node_name = &node_configuration.name; + match &node_configuration.config_type { + Some(ConfigType::LogConfig(LogConfiguration {})) => { + Ok(Box::new(logger::LogNode::new(node_name))) } - } - - /// Generate a description of a Node or pseudo-Node that is started with the given entrypoint. - /// (In practice, only Wasm nodes pay attention to the entrypoint). - pub fn node_subname(&self, entrypoint: &str) -> String { - match self { - Configuration::LogNode => "LogNode".to_string(), - Configuration::GrpcServerNode { .. } => "GrpcServerNode".to_string(), - Configuration::GrpcClientNode { .. } => "GrpcClientNode".to_string(), - Configuration::WasmNode { .. } => format!("WasmNode-{}", entrypoint), - Configuration::StorageNode { .. } => "StorageNode".to_string(), - Configuration::RoughtimeClientNode { .. } => "RoughtimeClientNode".to_string(), - Configuration::External => "ExternalPseudoNode".to_string(), + Some(ConfigType::GrpcServerConfig(config)) => { + Ok(Box::new(grpc::server::GrpcServerNode::new( + node_name, + config.clone(), + grpc_configuration + .grpc_server_tls_identity + .as_ref() + .expect("no gRPC server TLS identity provided to Oak Runtime") + .clone(), + )?)) + } + Some(ConfigType::WasmConfig(config)) => Ok(Box::new(wasm::WasmNode::new( + node_name, + application_configuration, + config.clone(), + )?)), + Some(ConfigType::GrpcClientConfig(config)) => { + Ok(Box::new(grpc::client::GrpcClientNode::new( + node_name, + config.clone(), + grpc_configuration + .grpc_client_root_tls_certificate + .as_ref() + .expect("no gRPC client root TLS certificate provided to Oak Runtime") + .clone(), + )?)) + } + Some(ConfigType::RoughtimeClientConfig(_config)) => { + Ok(Box::new(roughtime::RoughtimeClientNode::new(node_name))) + } + Some(ConfigType::StorageConfig(_config)) => { + Ok(Box::new(storage::StorageNode::new(node_name))) } + None => Err(ConfigurationError::InvalidNodeConfiguration), } } diff --git a/oak/server/rust/oak_runtime/src/node/wasm/mod.rs b/oak/server/rust/oak_runtime/src/node/wasm/mod.rs index 593f8387c7a..b613f03ff5f 100644 --- a/oak/server/rust/oak_runtime/src/node/wasm/mod.rs +++ b/oak/server/rust/oak_runtime/src/node/wasm/mod.rs @@ -15,12 +15,20 @@ // use crate::{ + node::ConfigurationError, runtime::{NodeReadStatus, RuntimeProxy}, NodeMessage, }; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; -use oak_abi::{label::Label, ChannelReadStatus, OakStatus}; +use oak_abi::{ + label::Label, + proto::oak::application::{ + ApplicationConfiguration, NodeConfiguration, WebAssemblyConfiguration, + }, + ChannelReadStatus, OakStatus, +}; +use prost::Message as _; use rand::RngCore; use std::{string::String, sync::Arc}; use tokio::sync::oneshot; @@ -97,22 +105,15 @@ impl WasmInterface { #[allow(clippy::too_many_arguments)] fn node_create( &self, - name_ptr: AbiPointer, - name_length: AbiPointerOffset, - entrypoint_ptr: AbiPointer, - entrypoint_length: AbiPointerOffset, + config_ptr: AbiPointer, + config_length: AbiPointerOffset, label_ptr: AbiPointer, label_length: AbiPointerOffset, initial_handle: oak_abi::Handle, ) -> Result<(), OakStatus> { debug!( - "{}: node_create({}, {}, {}, {}, {})", - self.pretty_name, - name_ptr, - name_length, - entrypoint_ptr, - entrypoint_length, - initial_handle + "{}: node_create({}, {}, {})", + self.pretty_name, config_ptr, config_length, initial_handle ); if self.runtime.is_terminating() { @@ -120,37 +121,20 @@ impl WasmInterface { return Err(OakStatus::ErrTerminated); } - let config_name_bytes = self + let config_bytes = self .get_memory() - .get(name_ptr, name_length as usize) + .get(config_ptr, config_length as usize) .map_err(|err| { error!( - "{}: node_create(): Unable to read name from guest memory: {:?}", + "{}: node_create(): Unable to read config from guest memory: {:?}", self.pretty_name, err ); OakStatus::ErrInvalidArgs })?; - let config_name = String::from_utf8(config_name_bytes).map_err(|err| { - error!( - "{}: node_create(): Unable to parse config_name: {:?}", - self.pretty_name, err - ); - OakStatus::ErrInvalidArgs - })?; - let entrypoint_bytes = self - .get_memory() - .get(entrypoint_ptr, entrypoint_length as usize) - .map_err(|err| { - error!( - "{}: node_create(): Unable to read entrypoint from guest memory: {:?}", - self.pretty_name, err - ); - OakStatus::ErrInvalidArgs - })?; - let entrypoint = String::from_utf8(entrypoint_bytes).map_err(|err| { - error!( - "{}: node_create(): Unable to parse entrypoint: {:?}", + let config = NodeConfiguration::decode(config_bytes.as_ref()).map_err(|err| { + warn!( + "{}: node_create(): Could not parse node configuration: {:?}", self.pretty_name, err ); OakStatus::ErrInvalidArgs @@ -175,12 +159,19 @@ impl WasmInterface { })?; debug!( - "{}: node_create('{}', '{}', {:?})", - self.pretty_name, config_name, entrypoint, label + "{}: node_create({:?}, {:?})", + self.pretty_name, config, label ); self.runtime - .node_create(&config_name, &entrypoint, &label, initial_handle) + .node_create(&config, &label, initial_handle) + .map_err(|err| { + error!( + "{}: node_create(): Could not create node: {:?}", + self.pretty_name, err + ); + err + }) } /// Corresponds to the host ABI function [`random_get`](https://github.com/project-oak/oak/blob/master/docs/abi.md#random_get). @@ -508,8 +499,6 @@ impl wasmi::Externals for WasmInterface { args.nth_checked(2)?, args.nth_checked(3)?, args.nth_checked(4)?, - args.nth_checked(5)?, - args.nth_checked(6)?, )), RANDOM_GET => { map_host_errors(self.random_get(args.nth_checked(0)?, args.nth_checked(1)?)) @@ -570,8 +559,6 @@ fn oak_resolve_func( &[ ABI_USIZE, // config_buf ABI_USIZE, // config_len - ABI_USIZE, // entrypoint_buf - ABI_USIZE, // entrypoint_len ABI_USIZE, // label_buf ABI_USIZE, // label_len ValueType::I64, // handle @@ -797,19 +784,32 @@ fn validate_entrypoint(module: &wasmi::Module, entrypoint: &str) -> Result<(), O pub struct WasmNode { node_name: String, module: Arc, - entrypoint: String, + entrypoint_name: String, } impl WasmNode { /// Creates a new [`WasmNode`] instance, but does not start it. /// May fail if the provided Wasm module is not valid. - pub fn new(node_name: &str, module: Arc, entrypoint: String) -> Option { - validate_entrypoint(&module, &entrypoint).ok()?; - - Some(Self { + pub fn new( + node_name: &str, + application_configuration: &ApplicationConfiguration, + node_configuration: WebAssemblyConfiguration, + ) -> Result { + let wasm_module_bytes = application_configuration + .wasm_modules + .get(&node_configuration.wasm_module_name) + .ok_or(ConfigurationError::IncorrectWebAssemblyModuleName)?; + let module = wasmi::Module::from_buffer(&wasm_module_bytes) + .map_err(ConfigurationError::WasmiModuleInializationError)?; + let entrypoint_name = node_configuration.wasm_entrypoint_name; + validate_entrypoint(&module, &entrypoint_name).map_err(|err| { + warn!("could not validate entrypoint: {:?}", err); + ConfigurationError::IncorrectWebAssemblyModuleName + })?; + Ok(Self { node_name: node_name.to_string(), - module, - entrypoint, + module: Arc::new(module), + entrypoint_name, }) } } @@ -824,7 +824,7 @@ impl super::Node for WasmNode { ) { debug!( "{}: running entrypoint '{}'", - self.node_name, self.entrypoint + self.node_name, self.entrypoint_name ); let wasi_stub = WasiStub; let mut abi = WasmInterface::new(&self.node_name, runtime); @@ -846,14 +846,14 @@ impl super::Node for WasmNode { instance .invoke_export( - &self.entrypoint, + &self.entrypoint_name, &[wasmi::RuntimeValue::I64(handle as i64)], &mut abi, ) .expect("failed to execute export"); debug!( "{}: entrypoint '{}' completed", - self.node_name, self.entrypoint + self.node_name, self.entrypoint_name ); } } diff --git a/oak/server/rust/oak_runtime/src/node/wasm/tests.rs b/oak/server/rust/oak_runtime/src/node/wasm/tests.rs index 8c13a05e623..cd8bd898db4 100644 --- a/oak/server/rust/oak_runtime/src/node/wasm/tests.rs +++ b/oak/server/rust/oak_runtime/src/node/wasm/tests.rs @@ -15,33 +15,39 @@ // use super::*; -use crate::{GrpcConfiguration, RuntimeProxy}; -use std::collections::HashMap; +use crate::{runtime::RuntimeProxy, GrpcConfiguration}; +use maplit::hashmap; +use oak_abi::{ + label::Label, + proto::oak::application::{ + node_configuration::ConfigType, ApplicationConfiguration, WebAssemblyConfiguration, + }, +}; use wat::{parse_file, parse_str}; -fn start_node>(buffer: S, entrypoint: &str) -> Result<(), OakStatus> { +fn start_node(wasm_module: Vec, entrypoint_name: &str) -> Result<(), OakStatus> { crate::runtime::tests::init_logging(); - let mut configuration = crate::runtime::Configuration { - nodes: HashMap::new(), - entry_module: "test_module".to_string(), - entrypoint: entrypoint.to_string(), + let module_name = "oak_module"; + let application_configuration = ApplicationConfiguration { + wasm_modules: hashmap! { module_name.to_string() => wasm_module }, + initial_node_configuration: None, }; - let module = wasmi::Module::from_buffer(buffer).unwrap(); - configuration.nodes.insert( - "test_module".to_string(), - crate::node::Configuration::WasmNode { - module: Arc::new(module), - }, - ); - let proxy = RuntimeProxy::create_runtime(configuration, GrpcConfiguration::default()); + let proxy = + RuntimeProxy::create_runtime(application_configuration, GrpcConfiguration::default()); let (_write_handle, read_handle) = proxy.channel_create(&Label::public_trusted())?; let result = proxy.node_create( - "test_module", - entrypoint, + &NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration { + wasm_module_name: module_name.to_string(), + wasm_entrypoint_name: entrypoint_name.to_string(), + })), + }, &oak_abi::label::Label::public_trusted(), read_handle, ); + proxy .channel_close(read_handle) .expect("could not close channel"); diff --git a/oak/server/rust/oak_runtime/src/proto/mod.rs b/oak/server/rust/oak_runtime/src/proto/mod.rs deleted file mode 100644 index 46adc806b3b..00000000000 --- a/oak/server/rust/oak_runtime/src/proto/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2019 The Project Oak Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -pub mod oak { - pub mod application { - include!(concat!(env!("OUT_DIR"), "/oak.application.rs")); - } -} diff --git a/oak/server/rust/oak_runtime/src/runtime/mod.rs b/oak/server/rust/oak_runtime/src/runtime/mod.rs index 2bd93e61d0b..525b992174b 100644 --- a/oak/server/rust/oak_runtime/src/runtime/mod.rs +++ b/oak/server/rust/oak_runtime/src/runtime/mod.rs @@ -26,6 +26,7 @@ use itertools::Itertools; use log::{debug, error, info, trace, warn}; use oak_abi::{ label::{Label, Tag}, + proto::oak::application::{ApplicationConfiguration, NodeConfiguration}, ChannelReadStatus, OakStatus, }; use prometheus::proto::MetricFamily; @@ -184,16 +185,6 @@ impl HtmlPath for (NodeId, oak_abi::Handle) { } } -/// Internal version of the [`ApplicationConfiguration`] protobuf, holding -/// the same information but parsed and verified. -/// -/// [`ApplicationConfiguration`]: crate::proto::oak::application::ApplicationConfiguration -pub struct Configuration { - pub nodes: HashMap, - pub entry_module: String, - pub entrypoint: String, -} - /// Helper types to indicate whether a channel read operation has succeed or has failed with not /// enough `bytes_capacity` and/or `handles_capacity`. pub enum NodeReadStatus { @@ -255,7 +246,7 @@ impl Drop for AuxServer { /// Runtime structure for configuring and running a set of Oak Nodes. pub struct Runtime { - application_configuration: Configuration, + application_configuration: ApplicationConfiguration, grpc_configuration: GrpcConfiguration, terminating: AtomicBool, @@ -352,11 +343,11 @@ impl Runtime { impl RuntimeProxy { /// Creates a [`Runtime`] instance with a single initial Node configured, and no channels. pub fn create_runtime( - configuration: Configuration, + application_configuration: ApplicationConfiguration, grpc_configuration: GrpcConfiguration, ) -> RuntimeProxy { let runtime = Arc::new(Runtime { - application_configuration: configuration, + application_configuration, grpc_configuration, terminating: AtomicBool::new(false), next_channel_id: AtomicU64::new(0), @@ -379,7 +370,7 @@ impl RuntimeProxy { proxy } - /// Configures and runs the protobuf specified Application [`Configuration`]. + /// Configures and runs the protobuf specified [`ApplicationConfiguration`]. /// /// After starting a [`Runtime`], calling [`Runtime::stop`] will notify all Nodes that they /// should terminate, and wait for them to terminate. @@ -388,17 +379,22 @@ impl RuntimeProxy { /// the configuration. pub fn start_runtime( &self, - runtime_config: crate::RuntimeConfiguration, + runtime_configuration: crate::RuntimeConfiguration, ) -> Result { - let module_name = self.runtime.application_configuration.entry_module.clone(); - let entrypoint = self.runtime.application_configuration.entrypoint.clone(); + let node_configuration = self + .runtime + .application_configuration + .initial_node_configuration + .as_ref() + .ok_or(OakStatus::ErrInvalidArgs)?; + self.metrics_data() .runtime_metrics .runtime_health_check .set(1); if cfg!(feature = "oak_debug") { - if let Some(port) = runtime_config.introspect_port { + if let Some(port) = runtime_configuration.introspect_port { self.runtime .aux_servers .lock() @@ -411,7 +407,7 @@ impl RuntimeProxy { )); } } - if let Some(port) = runtime_config.metrics_port { + if let Some(port) = runtime_configuration.metrics_port { self.runtime .aux_servers .lock() @@ -433,8 +429,7 @@ impl RuntimeProxy { ); self.node_create( - &module_name, - &entrypoint, + &node_configuration, // When first starting, we assign the least privileged label to the entry point Node. &Label::public_trusted(), read_handle, @@ -1249,11 +1244,11 @@ impl Runtime { fn node_create( self: Arc, node_id: NodeId, - config_name: &str, - entrypoint: &str, + config: &NodeConfiguration, label: &Label, initial_handle: oak_abi::Handle, ) -> Result<(), OakStatus> { + info!("creating node with config: {:?}", config); if self.is_terminating() { return Err(OakStatus::ErrTerminated); } @@ -1261,20 +1256,9 @@ impl Runtime { let reader = self.abi_to_read_half(node_id, initial_handle)?; - let config = self - .application_configuration - .nodes - .get(config_name) - .ok_or(OakStatus::ErrInvalidArgs)?; - let new_node_proxy = self.clone().proxy_for_new_node(); let new_node_id = new_node_proxy.node_id; - let new_node_name = format!( - "{}.{}({})", - config_name, - config.node_subname(entrypoint), - new_node_id.0 - ); + let new_node_name = format!("{}({})", config.name, new_node_id.0); self.node_configure_instance( new_node_id, &new_node_name, @@ -1283,22 +1267,18 @@ impl Runtime { ); let initial_handle = new_node_proxy .runtime - .new_abi_handle(new_node_proxy.node_id, reader.clone()); - debug!( - "{:?}: create node instance {:?} '{}'.'{}' with handle {} from {:?}", - node_id, new_node_id, config_name, entrypoint, initial_handle, reader - ); + .new_abi_handle(new_node_proxy.node_id, reader); - debug!("{:?}: create node instance {:?}", node_id, new_node_id); // This only creates a Node instance, but does not start it. - let instance = config - .create_node( - &new_node_name, - config_name, - entrypoint.to_owned(), - &self.grpc_configuration, - ) - .ok_or(OakStatus::ErrInvalidArgs)?; + let instance = node::create_node( + &self.application_configuration, + config, + &self.grpc_configuration, + ) + .map_err(|err| { + warn!("could not create node: {:?}", err); + OakStatus::ErrInvalidArgs + })?; debug!("{:?}: start node instance {:?}", node_id, new_node_id); let node_stopper = self.clone().node_start_instance( @@ -1320,7 +1300,7 @@ impl Runtime { fn node_start_instance( self: Arc, node_name: &str, - node_instance: Box, + node_instance: Box, node_proxy: RuntimeProxy, initial_handle: oak_abi::Handle, ) -> Result { @@ -1443,18 +1423,13 @@ impl RuntimeProxy { /// See [`Runtime::node_create`]. pub fn node_create( &self, - module_name: &str, - entrypoint: &str, + config: &NodeConfiguration, label: &Label, initial_handle: oak_abi::Handle, ) -> Result<(), OakStatus> { - self.runtime.clone().node_create( - self.node_id, - module_name, - entrypoint, - label, - initial_handle, - ) + self.runtime + .clone() + .node_create(self.node_id, config, label, initial_handle) } /// See [`Runtime::channel_create`]. diff --git a/oak/server/rust/oak_runtime/src/runtime/tests.rs b/oak/server/rust/oak_runtime/src/runtime/tests.rs index 1c4a1c3046f..ec8a79aa015 100644 --- a/oak/server/rust/oak_runtime/src/runtime/tests.rs +++ b/oak/server/rust/oak_runtime/src/runtime/tests.rs @@ -15,7 +15,10 @@ // use super::*; -use maplit::hashset; +use maplit::{hashmap, hashset}; +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, ApplicationConfiguration, LogConfiguration, NodeConfiguration, +}; use std::sync::Once; static LOG_INIT_ONCE: Once = Once::new(); @@ -33,12 +36,9 @@ type NodeBody = dyn Fn(RuntimeProxy) -> Result<(), OakStatus> + Send + Sync; /// instantiated by the [`Runtime`] with the provided [`Label`]. fn run_node_body(node_label: &Label, node_privilege: &NodePrivilege, node_body: Box) { init_logging(); - let configuration = crate::runtime::Configuration { - nodes: maplit::hashmap! { - "log".to_string() => crate::node::Configuration::LogNode, - }, - entry_module: "test_module".to_string(), - entrypoint: "test_function".to_string(), + let configuration = ApplicationConfiguration { + wasm_modules: hashmap! {}, + initial_node_configuration: None, }; info!("Create runtime for test"); let proxy = crate::RuntimeProxy::create_runtime(configuration, GrpcConfiguration::default()); @@ -262,14 +262,18 @@ fn create_node_same_label_ok() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&label_clone)?; - let result = runtime.node_create("log", "unused", &label_clone, read_handle); + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + }; + let result = runtime.node_create(&node_configuration, &label_clone, read_handle); assert_eq!(Ok(()), result); Ok(()) }), ); } -/// Create a test Node that creates a Node with a non-existing configuration name and fails. +/// Create a test Node that creates a Node with an invalid configuration and fails. #[test] fn create_node_invalid_configuration_err() { let label = test_label(); @@ -279,12 +283,12 @@ fn create_node_invalid_configuration_err() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&label_clone)?; - let result = runtime.node_create( - "invalid-configuration-name", - "unused", - &label_clone, - read_handle, - ); + // Node configuration without config type. + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: None, + }; + let result = runtime.node_create(&node_configuration, &label_clone, read_handle); assert_eq!(Err(OakStatus::ErrInvalidArgs), result); Ok(()) }), @@ -313,7 +317,11 @@ fn create_node_less_secret_label_err() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&initial_label_clone)?; - let result = runtime.node_create("log", "unused", &less_secret_label, read_handle); + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + }; + let result = runtime.node_create(&node_configuration, &less_secret_label, read_handle); assert_eq!(Err(OakStatus::ErrPermissionDenied), result); Ok(()) }), @@ -339,7 +347,11 @@ fn create_node_more_secret_label_ok() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&initial_label_clone)?; - let result = runtime.node_create("log", "unused", &more_secret_label, read_handle); + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + }; + let result = runtime.node_create(&node_configuration, &more_secret_label, read_handle); assert_eq!(Ok(()), result); Ok(()) }), diff --git a/oak/server/rust/oak_runtime/tests/config_tests.rs b/oak/server/rust/oak_runtime/tests/config_tests.rs deleted file mode 100644 index 1cc5eb27c98..00000000000 --- a/oak/server/rust/oak_runtime/tests/config_tests.rs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Copyright 2020 The Project Oak Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -use maplit::hashmap; - -use oak_runtime::proto::oak::application::{ - node_configuration::ConfigType::{LogConfig, WasmConfig}, - ApplicationConfiguration, LogConfiguration, NodeConfiguration, WebAssemblyConfiguration, -}; - -#[test] -fn test_app_config() { - let cfg = oak_runtime::application_configuration( - hashmap!["node".to_string() => vec![0x00, 0x01]], - "lumberjack", - "node", - "main", - ); - assert_eq!( - ApplicationConfiguration { - node_configs: vec![ - NodeConfiguration { - name: "node".to_string(), - config_type: Some(WasmConfig(WebAssemblyConfiguration { - module_bytes: vec![0, 1] - })) - }, - NodeConfiguration { - name: "lumberjack".to_string(), - config_type: Some(LogConfig(LogConfiguration {})) - } - ], - initial_node_config_name: "node".to_string(), - initial_entrypoint_name: "main".to_string(), - }, - cfg - ); -} - -#[test] -fn test_app_config_multi() { - let cfg = oak_runtime::application_configuration( - hashmap![ - "node".to_string() => vec![0x00, 0x01], - "another_node".to_string() => vec![0x02, 0x03], - ], - "lumberjack", - "node", - "main", - ); - assert_eq!( - ApplicationConfiguration { - node_configs: vec![ - NodeConfiguration { - name: "another_node".to_string(), - config_type: Some(WasmConfig(WebAssemblyConfiguration { - module_bytes: vec![2, 3] - })) - }, - NodeConfiguration { - name: "node".to_string(), - config_type: Some(WasmConfig(WebAssemblyConfiguration { - module_bytes: vec![0, 1] - })) - }, - NodeConfiguration { - name: "lumberjack".to_string(), - config_type: Some(LogConfig(LogConfiguration {})) - } - ], - initial_node_config_name: "node".to_string(), - initial_entrypoint_name: "main".to_string(), - }, - cfg - ); -} - -#[test] -fn test_app_config_no_logger() { - let cfg = oak_runtime::application_configuration( - hashmap!["node".to_string() => vec![0x00, 0x01]], - "", - "node", - "main", - ); - assert_eq!( - ApplicationConfiguration { - node_configs: vec![NodeConfiguration { - name: "node".to_string(), - config_type: Some(WasmConfig(WebAssemblyConfiguration { - module_bytes: vec![0, 1] - })) - },], - initial_node_config_name: "node".to_string(), - initial_entrypoint_name: "main".to_string(), - }, - cfg - ); -} diff --git a/oak/server/rust/oak_runtime/tests/integration_test.rs b/oak/server/rust/oak_runtime/tests/integration_test.rs index c805f294757..7817c798a94 100644 --- a/oak/server/rust/oak_runtime/tests/integration_test.rs +++ b/oak/server/rust/oak_runtime/tests/integration_test.rs @@ -20,7 +20,13 @@ mod common { use hyper::{Client, Uri}; use log::info; use maplit::hashmap; - use oak_abi::OakStatus; + use oak_abi::{ + proto::oak::application::{ + node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration, + WebAssemblyConfiguration, + }, + OakStatus, + }; use oak_runtime::{config, runtime::RuntimeProxy, GrpcConfiguration}; use wat::parse_str; @@ -36,18 +42,22 @@ mod common { let binary = parse_str(wat).expect("Could not parse wat module."); // Create a runtime with one node - let cfg = oak_runtime::application_configuration( - hashmap![ - "node".to_string() => binary, - ], - "lumberjack", - "node", - "oak_main", - ); + let application_configuration = ApplicationConfiguration { + wasm_modules: hashmap! { + "module".to_string() => binary, + }, + initial_node_configuration: Some(NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration { + wasm_module_name: "module".to_string(), + wasm_entrypoint_name: "oak_main".to_string(), + })), + }), + }; info!("Starting the runtime with one node."); config::configure_and_run( - cfg, + application_configuration, oak_runtime::RuntimeConfiguration { metrics_port: Some(crate::METRICS_PORT), introspect_port: None, @@ -68,7 +78,7 @@ mod common { info!("status: {}", res.status()); let buf = hyper::body::to_bytes(res).await?; - Ok(std::str::from_utf8(&buf[..]).unwrap().to_string()) + Ok(std::str::from_utf8(buf.as_ref()).unwrap().to_string()) } } diff --git a/sdk/rust/oak/src/grpc/client.rs b/sdk/rust/oak/src/grpc/client.rs index d46b78374a1..d5da64634a9 100644 --- a/sdk/rust/oak/src/grpc/client.rs +++ b/sdk/rust/oak/src/grpc/client.rs @@ -15,7 +15,7 @@ // use log::{info, warn}; -use oak_abi::label::Label; +use oak_abi::{label::Label, proto::oak::application::NodeConfiguration}; /// Client for a gRPC service in another Node. pub struct Client { @@ -25,8 +25,8 @@ pub struct Client { impl Client { /// Similar to [`Client::new_with_label`] but with a fixed label corresponding to "public /// trusted". - pub fn new(config_name: &str, entrypoint_name: &str) -> Option { - Client::new_with_label(config_name, entrypoint_name, &Label::public_trusted()) + pub fn new(config: &NodeConfiguration) -> Option { + Client::new_with_label(config, &Label::public_trusted()) } /// Creates a new Node that implements a gRPC service, and if successful return a Client. @@ -37,35 +37,20 @@ impl Client { /// proxy for a remote non-Oak gRPC service). /// /// The provided [`Label`] specifies the label for the newly created Node and Channel. - pub fn new_with_label( - config_name: &str, - entrypoint_name: &str, - label: &Label, - ) -> Option { + pub fn new_with_label(config: &NodeConfiguration, label: &Label) -> Option { let (invocation_sender, invocation_receiver) = crate::io::channel_create().expect("failed to create channel"); - let status = crate::node_create_with_label( - config_name, - entrypoint_name, - label, - invocation_receiver.handle, - ); + let status = crate::node_create_with_label(config, label, invocation_receiver.handle); invocation_receiver .close() .expect("failed to close channel"); match status { Ok(_) => { - info!( - "Client created for '{}' in '{}'", - entrypoint_name, config_name - ); + info!("Client created for '{:?}'", config); Some(Client { invocation_sender }) } Err(status) => { - warn!( - "failed to create gRPC client pseudo-Node for '{}' in '{}' : {:?}", - entrypoint_name, config_name, status - ); + warn!("failed to create Client for '{:?}': {:?}", config, status); None } } diff --git a/sdk/rust/oak/src/grpc/server.rs b/sdk/rust/oak/src/grpc/server.rs index 52254c5eacd..7f26cee6ed4 100644 --- a/sdk/rust/oak/src/grpc/server.rs +++ b/sdk/rust/oak/src/grpc/server.rs @@ -14,27 +14,20 @@ // limitations under the License. // -//! Functionality to help Oak Nodes create gRPC pseudo-Nodes. +//! Functionality to help Oak Nodes create gRPC server pseudo-Nodes. use crate::{OakStatus, ReadHandle}; -/// Default name for predefined Node configuration that corresponds to a gRPC pseudo-Node. -pub const DEFAULT_CONFIG_NAME: &str = "grpc-server"; - -/// Initialize a gRPC pseudo-Node with the default configuration. -pub fn init_default() -> ReadHandle { - init(DEFAULT_CONFIG_NAME).expect("Coundn't create gRPC server pseudo-Node") -} - -/// Initializes a gRPC server pseudo-Node and passes it a handle to write invocations to. +/// Initializes a gRPC server pseudo-Node listening on the provided address. /// /// Returns a [`ReadHandle`] to read invocations from. /// /// [`ReadHandle`]: crate::ReadHandle -pub fn init(config: &str) -> std::result::Result { +pub fn init(address: &str) -> std::result::Result { // Create a channel and pass the read half to a new gRPC pseudo-Node. let (write_handle, read_handle) = crate::channel_create().expect("Couldn't create a channel"); - crate::node_create(config, "oak_main", read_handle)?; + let config = crate::node_config::grpc_server(address); + crate::node_create(&config, read_handle)?; crate::channel_close(read_handle.handle).expect("Couldn't close a channel"); // Create a separate channel for receiving invocations and pass it to a gRPC pseudo-Node. diff --git a/sdk/rust/oak/src/lib.rs b/sdk/rust/oak/src/lib.rs index 5a0a73ed961..1b4a031d0a6 100644 --- a/sdk/rust/oak/src/lib.rs +++ b/sdk/rust/oak/src/lib.rs @@ -16,6 +16,8 @@ use byteorder::{ReadBytesExt, WriteBytesExt}; use log::{debug, error, info, warn}; +use oak_abi::proto::oak::application::NodeConfiguration; +use prost::Message; use serde::{Deserialize, Serialize}; // Re-export ABI constants that are also visible as part of the SDK API. @@ -29,6 +31,7 @@ pub use error::OakError; pub mod grpc; pub mod io; pub mod logger; +pub mod node_config; pub mod rand; pub mod roughtime; pub mod storage; @@ -37,7 +40,7 @@ pub mod proto { pub mod oak { // The storage protobuf messages use the label.Label type which is built // in the `oak_abi` crate, so make it available here too. - use oak_abi::proto::oak::label; + pub use oak_abi::proto::oak::{application, label}; pub mod storage { include!(concat!(env!("OUT_DIR"), "/oak.storage.rs")); } @@ -351,12 +354,8 @@ pub fn channel_close(handle: Handle) -> Result<(), OakStatus> { } /// Similar to [`node_create_with_label`], but with a fixed label corresponding to "public trusted". -pub fn node_create( - config_name: &str, - entrypoint_name: &str, - half: ReadHandle, -) -> Result<(), OakStatus> { - node_create_with_label(config_name, entrypoint_name, &Label::public_trusted(), half) +pub fn node_create(config: &NodeConfiguration, half: ReadHandle) -> Result<(), OakStatus> { + node_create_with_label(config, &Label::public_trusted(), half) } /// Creates a new Node running the configuration identified by `config_name`, running the entrypoint @@ -368,18 +367,20 @@ pub fn node_create( /// /// See https://github.com/project-oak/oak/blob/master/docs/concepts.md#labels pub fn node_create_with_label( - config_name: &str, - entrypoint_name: &str, + config: &NodeConfiguration, label: &Label, half: ReadHandle, ) -> Result<(), OakStatus> { let label_bytes = label.serialize(); + let mut config_bytes = Vec::new(); + config.encode(&mut config_bytes).map_err(|err| { + warn!("Could not encode node configuration: {:?}", err); + OakStatus::ErrInvalidArgs + })?; let status = unsafe { oak_abi::node_create( - config_name.as_ptr(), - config_name.len(), - entrypoint_name.as_ptr(), - entrypoint_name.len(), + config_bytes.as_ptr(), + config_bytes.len(), label_bytes.as_ptr(), label_bytes.len(), half.handle.id, @@ -425,7 +426,7 @@ pub fn set_panic_hook() { let msg = match payload.downcast_ref::<&'static str>() { Some(content) => *content, None => match payload.downcast_ref::() { - Some(content) => &content[..], + Some(content) => content.as_ref(), None => "", }, }; @@ -540,7 +541,8 @@ pub fn run_event_loop>( /// /// oak::entrypoint!(dummy => |_in_channel| { /// let dispatcher = DummyNode::default(); -/// let grpc_channel = oak::grpc::server::init_default(); +/// let grpc_channel = oak::grpc::server::init("[::]:8080") +/// .expect("could not create gRPC server pseudo-node"); /// oak::run_event_loop(dispatcher, grpc_channel); /// }); /// @@ -576,7 +578,8 @@ pub fn run_event_loop>( /// oak::entrypoint!(its_complicated => |_in_channel| { /// init_all_the_things(); /// let dispatcher = DummyNode::default(); -/// let grpc_channel = oak::grpc::server::init_default(); +/// let grpc_channel = oak::grpc::server::init("[::]:8080") +/// .expect("could not create gRPC server pseudo-node"); /// oak::run_event_loop(dispatcher, grpc_channel); /// }); /// # diff --git a/sdk/rust/oak/src/logger/mod.rs b/sdk/rust/oak/src/logger/mod.rs index f2264876659..db107bb4dc0 100644 --- a/sdk/rust/oak/src/logger/mod.rs +++ b/sdk/rust/oak/src/logger/mod.rs @@ -60,10 +60,6 @@ fn map_level(level: Level) -> oak_abi::proto::oak::log::Level { } } -/// Default name for predefined Node configuration that corresponds to a logging -/// pseudo-Node. -pub const DEFAULT_CONFIG_NAME: &str = "log"; - /// Initialize Node-wide default logging. /// /// Uses the default level (`Debug`) and the default pre-defined name @@ -73,7 +69,7 @@ pub const DEFAULT_CONFIG_NAME: &str = "log"; /// /// Panics if a logger has already been set. pub fn init_default() { - init(Level::Debug, DEFAULT_CONFIG_NAME).unwrap(); + init(Level::Debug).unwrap(); } /// Initialize Node-wide logging via a channel to a logging pseudo-Node. @@ -84,10 +80,10 @@ pub fn init_default() { /// # Errors /// /// An error is returned if a logger has already been set. -pub fn init(level: Level, config: &str) -> Result<(), SetLoggerError> { +pub fn init(level: Level) -> Result<(), SetLoggerError> { // Create a channel and pass the read half to a fresh logging Node. let (write_handle, read_handle) = crate::channel_create().expect("could not create channel"); - crate::node_create(config, "oak_main", read_handle).expect("could not create node"); + crate::node_create(&crate::node_config::log(), read_handle).expect("could not create node"); crate::channel_close(read_handle.handle).expect("could not close channel"); log::set_boxed_logger(Box::new(OakChannelLogger { diff --git a/sdk/rust/oak/src/node_config.rs b/sdk/rust/oak/src/node_config.rs new file mode 100644 index 00000000000..3398d390629 --- /dev/null +++ b/sdk/rust/oak/src/node_config.rs @@ -0,0 +1,58 @@ +// +// Copyright 2020 The Project Oak Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//! Helper methods for creating common [`NodeConfiguration`] instances. + +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, GrpcClientConfiguration, GrpcServerConfiguration, + LogConfiguration, NodeConfiguration, WebAssemblyConfiguration, +}; + +pub fn grpc_client(address: &str) -> NodeConfiguration { + NodeConfiguration { + name: "grpc_client".to_string(), + config_type: Some(ConfigType::GrpcClientConfig(GrpcClientConfiguration { + address: address.to_string(), + uri: address.to_string(), + })), + } +} + +pub fn grpc_server(address: &str) -> NodeConfiguration { + NodeConfiguration { + name: "grpc_server".to_string(), + config_type: Some(ConfigType::GrpcServerConfig(GrpcServerConfiguration { + address: address.to_string(), + })), + } +} + +pub fn wasm(module_name: &str, entrypoint_name: &str) -> NodeConfiguration { + NodeConfiguration { + name: format!("wasm.{}.{}", module_name, entrypoint_name), + config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration { + wasm_module_name: module_name.to_string(), + wasm_entrypoint_name: entrypoint_name.to_string(), + })), + } +} + +pub fn log() -> NodeConfiguration { + NodeConfiguration { + name: "log".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + } +} diff --git a/sdk/rust/oak/src/roughtime/mod.rs b/sdk/rust/oak/src/roughtime/mod.rs index 3ef468e8f07..466d7d45da4 100644 --- a/sdk/rust/oak/src/roughtime/mod.rs +++ b/sdk/rust/oak/src/roughtime/mod.rs @@ -20,10 +20,9 @@ use crate::{ grpc, proto::oak::roughtime::{RoughTimeRequest, RoughtimeServiceClient}, }; - -/// Default name for predefined Node config that corresponds to a Roughtime -/// pseudo-Node. -pub const DEFAULT_CONFIG_NAME: &str = "roughtime"; +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, NodeConfiguration, RoughtimeClientConfiguration, +}; /// Local representation of the connection to an external Roughtime service. pub struct Roughtime { @@ -31,16 +30,13 @@ pub struct Roughtime { } impl Roughtime { - /// Create a default `Roughtime` instance assuming the default pre-defined - /// name (`"roughtime"`) identifying Roughtime pseudo-Node config. - pub fn default() -> Option { - Roughtime::new(DEFAULT_CONFIG_NAME) - } - - /// Create a `Roughtime` instance using the given name identifying Roughtime - /// pseudo-Node configuration. - pub fn new(config: &str) -> Option { - crate::grpc::client::Client::new(config, "oak_main").map(|client| Roughtime { + /// Creates a [`Roughtime`] instance using the given configuration. + pub fn new(config: &RoughtimeClientConfiguration) -> Option { + let config = NodeConfiguration { + name: "roughtime".to_string(), + config_type: Some(ConfigType::RoughtimeClientConfig(config.clone())), + }; + crate::grpc::client::Client::new(&config).map(|client| Roughtime { client: RoughtimeServiceClient(client), }) } diff --git a/sdk/rust/oak/src/storage/mod.rs b/sdk/rust/oak/src/storage/mod.rs index 1cd96e3a981..c43cd59f349 100644 --- a/sdk/rust/oak/src/storage/mod.rs +++ b/sdk/rust/oak/src/storage/mod.rs @@ -23,10 +23,9 @@ use crate::{ StorageItem, StorageServiceClient, }, }; - -/// Default name for predefined Node config that corresponds to a storage -/// pseudo-Node. -pub const DEFAULT_CONFIG_NAME: &str = "storage"; +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, NodeConfiguration, StorageProxyConfiguration, +}; /// Local representation of the connection to an external storage service. pub struct Storage { @@ -34,16 +33,13 @@ pub struct Storage { } impl Storage { - /// Create a default `Storage` instance assuming the default pre-defined - /// name (`"storage"`) identifying storage Node config. - pub fn default() -> Option { - Storage::new(DEFAULT_CONFIG_NAME) - } - - /// Create a `Storage` instance using the given name identifying storage - /// Node configuration. - pub fn new(config: &str) -> Option { - crate::grpc::client::Client::new(config, "oak_main").map(|client| Storage { + /// Creates a [`Storage`] instance using the given configuration. + pub fn new(config: &StorageProxyConfiguration) -> Option { + crate::grpc::client::Client::new(&NodeConfiguration { + name: "storage".to_string(), + config_type: Some(ConfigType::StorageConfig(config.clone())), + }) + .map(|client| Storage { client: StorageServiceClient(client), }) } diff --git a/sdk/rust/oak/src/stubs.rs b/sdk/rust/oak/src/stubs.rs index d6db925ee3c..cd9f287bd82 100644 --- a/sdk/rust/oak/src/stubs.rs +++ b/sdk/rust/oak/src/stubs.rs @@ -72,13 +72,7 @@ pub extern "C" fn channel_close(_handle: u64) -> u32 { panic!("stub function invoked!"); } #[no_mangle] -pub extern "C" fn node_create( - _config_buf: *const u8, - _config_len: usize, - _entrypoint_buf: *const u8, - _entrypoint_len: usize, - _handle: u64, -) -> u32 { +pub extern "C" fn node_create(_config_buf: *const u8, _config_len: usize, _handle: u64) -> u32 { panic!("stub function invoked!"); } #[no_mangle] diff --git a/sdk/rust/oak_tests/Cargo.toml b/sdk/rust/oak_tests/Cargo.toml index 410bd703c67..3fcfc29a77d 100644 --- a/sdk/rust/oak_tests/Cargo.toml +++ b/sdk/rust/oak_tests/Cargo.toml @@ -7,6 +7,7 @@ license = "Apache-2.0" [dependencies] byteorder = "*" +cargo_metadata = "*" lazy_static = "*" log = { version = "*", features = ["std"] } oak = "=0.1.0" @@ -14,4 +15,4 @@ oak_abi = "=0.1.0" oak_runtime = { version = "=0.1.0", features = ["test_build"] } prost = "*" rand = { version = "*" } -cargo_metadata = "*" +tonic = { version = "*", features = ["tls"] } diff --git a/sdk/rust/oak_tests/src/lib.rs b/sdk/rust/oak_tests/src/lib.rs index cec613760e3..8016f455dbe 100644 --- a/sdk/rust/oak_tests/src/lib.rs +++ b/sdk/rust/oak_tests/src/lib.rs @@ -17,8 +17,13 @@ //! Test utilities to help with unit testing of Oak SDK code. use log::{debug, info}; +use oak_abi::proto::oak::application::{ + node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration, + WebAssemblyConfiguration, +}; use prost::Message; use std::{collections::HashMap, process::Command}; +use tonic::transport::Certificate; // TODO(#544): re-enable unit tests of SDK functionality @@ -45,21 +50,20 @@ pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result std::fs::read(path) } -const DEFAULT_LOG_CONFIG_NAME: &str = "log"; const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main"; const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml"; const MODULE_WASM_SUFFIX: &str = ".wasm"; -/// Convenience helper to build and run a single-Node Application with the -/// given module name, using the default name "oak_main" for its entrypoint. +/// Convenience helper to build and run a single-Node Application with the given module name, using +/// the default name "oak_main" for its entrypoint. pub fn run_single_module_default( module_config_name: &str, ) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> { run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME) } -/// Convenience helper to build and run a single-Node application with the -/// given module name, using the provided entrypoint name. +/// Convenience helper to build and run a single-Node application with the given module name, using +/// the provided entrypoint name. pub fn run_single_module( module_config_name: &str, entrypoint_name: &str, @@ -76,23 +80,33 @@ pub fn run_single_module( .cloned() .collect(); - let configuration = oak_runtime::application_configuration( - wasm, - DEFAULT_LOG_CONFIG_NAME, - module_config_name, - entrypoint_name, - ); + let application_configuration = ApplicationConfiguration { + wasm_modules: wasm, + initial_node_configuration: Some(NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration { + wasm_module_name: module_config_name.to_string(), + wasm_entrypoint_name: entrypoint_name.to_string(), + })), + }), + }; oak_runtime::configure_and_run( - configuration, + application_configuration, oak_runtime::RuntimeConfiguration::default(), - oak_runtime::GrpcConfiguration::default(), + oak_runtime::GrpcConfiguration { + grpc_server_tls_identity: None, + // Some of the tests require a gRPC client, so we populate the required certificate with + // an invalid value here, even though it will still fail when instantiating the actual + // gRPC client. + grpc_client_root_tls_certificate: Some(Certificate::from_pem("invalid-cert")), + }, ) } // TODO(#543): move this to oak_runtime as it's not test-specific pub fn grpc_request( - proxy: &oak_runtime::runtime::RuntimeProxy, + proxy: &oak_runtime::RuntimeProxy, handle: oak_abi::Handle, method_name: &str, req: &R,