From 7d4ab4a8075604579ccd5a30124cdca56906be46 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Wed, 29 Apr 2020 18:46:49 +0100 Subject: [PATCH] Move node configuration to runtime The ApplicationConfiguration object is now more of a directory of executable wasm modules, and an initial Node configuration. Subsequent Nodes are created at runtime by passing the serialized NodeConfiguration protobuf message to `node_create` directly. This allows removing an extra indirection from application to the config of individual nodes, in favour of inlining configs in the Wasm code of the main node itself. Also remove parts of the logic around the old ApplicationConfiguration format from the C++ Oak Runtime, under the assumption that it is going to be deleted soon. Ref #688 --- Cargo.lock | 69 +----- docs/abi.md | 23 +- docs/concepts.md | 11 +- docs/programming-oak.md | 86 ++++---- examples/abitest/abitest_common/src/lib.rs | 3 - examples/abitest/config/BUILD | 4 +- examples/abitest/config/config.textproto | 63 +----- examples/abitest/module_0/rust/src/lib.rs | 203 +++++++++--------- examples/abitest/module_1/rust/src/lib.rs | 5 +- examples/abitest/tests/Cargo.toml | 1 + examples/abitest/tests/src/tests.rs | 42 ++-- examples/aggregator/backend/Cargo.toml | 2 +- examples/aggregator/config/config.textproto | 28 +-- examples/aggregator/module/rust/src/lib.rs | 9 +- examples/chat/config/config.textproto | 21 +- examples/chat/module/rust/Cargo.toml | 1 + examples/chat/module/rust/src/lib.rs | 6 +- examples/hello_world/config/BUILD | 2 +- examples/hello_world/config/config.textproto | 27 +-- .../config/config_no_translator.textproto | 18 -- .../hello_world/module/cpp/hello_world.cc | 4 +- examples/hello_world/module/rust/src/lib.rs | 7 +- .../machine_learning/config/config.textproto | 21 +- .../machine_learning/module/rust/src/lib.rs | 3 +- .../config/config.textproto | 21 +- .../module/rust/src/lib.rs | 3 +- .../running_average/config/config.textproto | 21 +- .../running_average/module/rust/src/lib.rs | 3 +- examples/rustfmt/config/config.textproto | 21 +- examples/rustfmt/module/rust/src/lib.rs | 3 +- examples/tensorflow/config/config.textproto | 21 +- examples/translator/config/config.textproto | 21 +- examples/translator/module/rust/src/lib.rs | 3 +- oak/common/BUILD | 18 -- oak/common/app_config.cc | 25 +-- oak/common/app_config_serializer.cc | 48 ++--- oak/common/app_config_test.cc | 100 --------- oak/common/testdata/barenode.textproto | 12 -- oak/common/testdata/default.textproto | 22 -- oak/common/testdata/dup_config_name.textproto | 16 -- oak/common/testdata/dup_wasm.textproto | 18 -- oak/common/testdata/grpcclient.textproto | 18 -- oak/common/testdata/grpcport.textproto | 12 -- oak/common/testdata/lognode.textproto | 16 -- oak/common/testdata/missing_wasm.textproto | 16 -- oak/common/testdata/no_wasm_main.textproto | 21 -- oak/common/testdata/storagenode.textproto | 18 -- oak/common/testdata/two_log.textproto | 20 -- oak/module/oak_abi.h | 5 +- oak/module/oak_main.h | 16 +- oak/proto/application.proto | 72 ++++--- oak/server/rust/oak_abi/build.rs | 1 + oak/server/rust/oak_abi/src/lib.rs | 16 +- oak/server/rust/oak_abi/src/proto/mod.rs | 4 + oak/server/rust/oak_glue/src/lib.rs | 14 +- oak/server/rust/oak_loader/Cargo.toml | 1 + oak/server/rust/oak_loader/src/main.rs | 6 +- oak/server/rust/oak_runtime/build.rs | 22 -- oak/server/rust/oak_runtime/src/config.rs | 131 +---------- oak/server/rust/oak_runtime/src/lib.rs | 4 +- .../rust/oak_runtime/src/node/grpc/client.rs | 36 +++- .../rust/oak_runtime/src/node/grpc/server.rs | 28 ++- oak/server/rust/oak_runtime/src/node/mod.rs | 199 +++++------------ .../rust/oak_runtime/src/node/wasm/mod.rs | 104 ++++----- .../rust/oak_runtime/src/node/wasm/tests.rs | 40 ++-- oak/server/rust/oak_runtime/src/proto/mod.rs | 21 -- .../rust/oak_runtime/src/runtime/mod.rs | 93 +++----- .../rust/oak_runtime/src/runtime/tests.rs | 46 ++-- .../rust/oak_runtime/tests/config_tests.rs | 112 ---------- .../oak_runtime/tests/integration_test.rs | 32 ++- sdk/rust/oak/src/grpc/client.rs | 29 +-- sdk/rust/oak/src/grpc/server.rs | 17 +- sdk/rust/oak/src/lib.rs | 35 +-- sdk/rust/oak/src/logger/mod.rs | 10 +- sdk/rust/oak/src/node_config.rs | 58 +++++ sdk/rust/oak/src/roughtime/mod.rs | 24 +-- sdk/rust/oak/src/storage/mod.rs | 24 +-- sdk/rust/oak/src/stubs.rs | 8 +- sdk/rust/oak_tests/Cargo.toml | 3 +- sdk/rust/oak_tests/src/lib.rs | 42 ++-- 80 files changed, 802 insertions(+), 1607 deletions(-) delete mode 100644 examples/hello_world/config/config_no_translator.textproto delete mode 100644 oak/common/app_config_test.cc delete mode 100644 oak/common/testdata/barenode.textproto delete mode 100644 oak/common/testdata/default.textproto delete mode 100644 oak/common/testdata/dup_config_name.textproto delete mode 100644 oak/common/testdata/dup_wasm.textproto delete mode 100644 oak/common/testdata/grpcclient.textproto delete mode 100644 oak/common/testdata/grpcport.textproto delete mode 100644 oak/common/testdata/lognode.textproto delete mode 100644 oak/common/testdata/missing_wasm.textproto delete mode 100644 oak/common/testdata/no_wasm_main.textproto delete mode 100644 oak/common/testdata/storagenode.textproto delete mode 100644 oak/common/testdata/two_log.textproto delete mode 100644 oak/server/rust/oak_runtime/build.rs delete mode 100644 oak/server/rust/oak_runtime/src/proto/mod.rs delete mode 100644 oak/server/rust/oak_runtime/tests/config_tests.rs create mode 100644 sdk/rust/oak/src/node_config.rs 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/concepts.md b/docs/concepts.md index cb9e7ab65cf..fbad6007438 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -362,17 +362,18 @@ and connected by unidirectional channels. The initial connectivity graph consists of a single Application Node with no associated channel. Once the Application is running, new Nodes can be created (with -`node_create()`), new channels can be created (with `channel_create()`) and the -handles to either half of the channel may be passed between Nodes (over -channels). +[`node_create`](/docs/abi.md#node_create)), new channels can be created (with +[`channel_create`](/docs/abi.md#channel_create)) and the handles to either half +of the channel may be passed between Nodes (over channels). Typically, the first Application Node creates a [gRPC server pseudo-Node](#pseudo-nodes) and sets up a channel from the pseudo-Node to the Application, on which [gRPC method invocations](#invocations) are delivered. -The allowed contents of the Nodes, and the initial Node to run, are specified by -an [Application Configuration](/oak/proto/application.proto). +The list of allowed WebAssembly modules that can be used to instantiate +WebAssembly Nodes, and the initial Node to run, are specified by an +[Application Configuration](/oak/proto/application.proto). Once a new Oak Application is initialized and its gRPC endpoint available, clients may connect to it using individually end-to-end encrypted, authenticated diff --git a/docs/programming-oak.md b/docs/programming-oak.md index bf93cb4c045..3490fc5afce 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); } ``` @@ -210,15 +212,11 @@ simple access to the service. ## Running an Oak Application -In order to run the Oak Application, each of the Nodes that comprise the -Application must first be compiled into one or more WebAssembly modules, and +In order to run the Oak Application, each of the WebAssembly Nodes that comprise +the Application must first be compiled into one or more WebAssembly modules, and these compiled WebAssembly modules are then assembled into an overall Application Configuration File. -The Application Configuration also includes the port that the Oak Server should -use for its gRPC service to appear on; the resulting (host:port) service -endpoint is connected to by any clients of the Application. - Each of these steps is described in the following sections. ### Creating a Configuration File @@ -228,37 +226,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: "" - } -} -node_configs { - name: "translator" - wasm_config { - module_bytes: "" - } -} -node_configs { - name: "grpc-server" - grpc_server_config { - address: "[::]:8080" +initial_node_configuration: { + name: "main" + wasm_config: { + wasm_module_name: "app" + wasm_entrypoint_name: "grpc_oak_main" } } -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 it is 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 +403,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 +522,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 +536,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/BUILD b/examples/hello_world/config/BUILD index cdc16591501..845841a9a77 100644 --- a/examples/hello_world/config/BUILD +++ b/examples/hello_world/config/BUILD @@ -37,5 +37,5 @@ serialized_config( modules = { "app": "//examples/hello_world/module/cpp:hello_world.wasm", }, - textproto = ":config_no_translator.textproto", + textproto = ":config.textproto", ) 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/config/config_no_translator.textproto b/examples/hello_world/config/config_no_translator.textproto deleted file mode 100644 index 40a0bd4bbd9..00000000000 --- a/examples/hello_world/config/config_no_translator.textproto +++ /dev/null @@ -1,18 +0,0 @@ -node_configs { - name: "app" - 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/cpp/hello_world.cc b/examples/hello_world/module/cpp/hello_world.cc index ddf4d5f7b0d..c2b1be3cd2d 100644 --- a/examples/hello_world/module/cpp/hello_world.cc +++ b/examples/hello_world/module/cpp/hello_world.cc @@ -28,12 +28,12 @@ extern "C" void process_invocation(const uint8_t* _req_buf, uint32_t _req_size, // Manually create an encapsulated GrpcResponse protobuf and send it back. // 0a b00001.010 = tag 1 (GrpcResponse.rsp_msg), length-delimited field // 0b length=11 - // 0A b00001.010 = tag 1 (HelloResponse.reply), length-delimited field + // 0a b00001.010 = tag 1 (HelloResponse.reply), length-delimited field // 07 length=7 // 74657374696e67 "testing" // 18 b00011.000 = tag 3 (GrpcResponse.last), varint // 01 true - uint8_t rsp_buf[] = "\x0a\x0b\x0A\x07\x74\x65\x73\x74\x69\x6e\x67\x18\x01"; + uint8_t rsp_buf[] = "\x0a\x0b\x0a\x07\x74\x65\x73\x74\x69\x6e\x67\x18\x01"; // TODO(#422): replace with use of message type and serialization. channel_write(rsp_handle, rsp_buf, sizeof(rsp_buf) - 1, nullptr, 0); } 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 40a0bd4bbd9..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: "grpc_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/module/oak_abi.h b/oak/module/oak_abi.h index 8d7664599c3..7dbe1691730 100644 --- a/oak/module/oak_abi.h +++ b/oak/module/oak_abi.h @@ -54,9 +54,8 @@ WASM_IMPORT("oak") oak_abi::OakStatus channel_create(oak_abi::Handle* write_handle, oak_abi::Handle* read_handle, uint8_t* label_buf, size_t label_size); WASM_IMPORT("oak") -oak_abi::OakStatus node_create(uint8_t* config_buf, size_t config_size, uint8_t* entrypoint_buf, - size_t entrypoint_size, uint8_t* label_buf, size_t label_size, - oak_abi::Handle handle); +oak_abi::OakStatus node_create(uint8_t* config_buf, size_t config_size, uint8_t* label_buf, + size_t label_size, oak_abi::Handle handle); WASM_IMPORT("oak") oak_abi::OakStatus random_get(uint8_t* buf, size_t buf_size); diff --git a/oak/module/oak_main.h b/oak/module/oak_main.h index 224101cb45e..fda641c09cc 100644 --- a/oak/module/oak_main.h +++ b/oak/module/oak_main.h @@ -44,9 +44,19 @@ WASM_EXPORT void grpc_oak_main(oak_abi::Handle _handle) { } // Create a gRPC server pseudo-Node - char config_name[] = "grpc-server"; - result = node_create((uint8_t*)config_name, sizeof(config_name) - 1, nullptr, 0, nullptr, 0, - read_handle); + // Manually create an encapsulated NodeConfiguration protobuf and send it back. + // 0a b00001.010 = tag 1 (NodeConfiguration.name), length-delimited field + // 0b length=11 + // 677270635f736572 "grpc_server" + // 2a b00101.010 = tag 5 (NodeConfiguration.grpc_server_config) + // 0b length=11 + // 0a b00001.010 = tag 1 (GrpcServerConfiguration.address) + // 09 length=9 + // 5b3a3a5d3a38303830 "[::]:8080" + char node_config[] = + "\x0a\x0b\x67\x72\x70\x63\x5f\x73\x65\x72\x76\x65\x72\x2a\x0b\x0a\x09\x5b\x3a\x3a\x5d\x3a\x38" + "\x30\x38\x30"; + result = node_create((uint8_t*)node_config, sizeof(node_config) - 1, nullptr, 0, read_handle); if (result != oak_abi::OakStatus::OK) { return; } diff --git a/oak/proto/application.proto b/oak/proto/application.proto index d4f8e1472fa..3b8d243c114 100644 --- a/oak/proto/application.proto +++ b/oak/proto/application.proto @@ -20,27 +20,41 @@ package oak.application; // An ApplicationConfiguration represents a unit of deployment in Oak. // -// 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 -// the exception of the specified initial Node (which is created by the -// Oak runtime). +// A running Oak Application instance is built from a collection of +// interconnected Nodes. +// +// Nodes are created dynamically at runtime, with the exception of the specified +// initial Node (which is created by the Oak runtime when instantiating the Oak +// Application). +// +// WebAssembly Nodes in particular can only run the code described by an entry +// in this configuration. 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; @@ -51,13 +65,16 @@ message NodeConfiguration { } } -// WebAssemblyConfiguration describes the configuration of a Web Assembly based Node. +// 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 @@ -85,8 +102,8 @@ message GrpcServerConfiguration { // pseudo-Node (which is provided by the Oak Runtime), connected to a specific // external (non-Oak) gRPC service. message GrpcClientConfiguration { - // The URI component of a gRPC server endpoint. Must contain the "Host" element. - // https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html + // The URI component of a gRPC server endpoint. Must contain the "Host" + // element. https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html string uri = 1; // The endpoint address of the external gRPC service. // `address` is represented as an "ip_address:tcp_port" string. @@ -117,13 +134,14 @@ message RoughtimeServer { string public_key_base64 = 4; } -// A serialized list of key-value pairs that are specified as command line flags to the Oak Loader -// binary, and are made available to the initial Node of the running Oak Application. +// A serialized list of key-value pairs that are specified as command line flags +// to the Oak Loader binary, and are made available to the initial Node of the +// running Oak Application. // // Keys are human readable strings and usually correspond to file names. // -// Values are raw binary blobs and usually correspond to file contents, which must be interpreted by -// the running Oak Application. +// Values are raw binary blobs and usually correspond to file contents, which +// must be interpreted by the running Oak Application. message ConfigMap { map items = 1; } 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 55a757be4a1..9d603de57de 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 18f0acb3ce2..5a460e56a34 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..80785b624e5 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,17 @@ 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. +// TODO(#1027): Improve or delete this enum. #[derive(Debug)] pub enum ConfigurationError { AddressParsingError(AddrParseError), IncorrectPort, + IncorrectURI, NoHostElement, CertificateParsingError, + IncorrectWebAssemblyModuleName, + InvalidNodeConfiguration, WasmiModuleInializationError(wasmi::Error), } @@ -108,10 +70,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 +86,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 f28a7a5c2e8..89a208439e2 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 98b4cdcd549..1f5f92bcdbc 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,8 +317,12 @@ fn create_node_less_confidential_label_err() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&initial_label_clone)?; + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + }; let result = - runtime.node_create("log", "unused", &less_confidential_label, read_handle); + runtime.node_create(&node_configuration, &less_confidential_label, read_handle); assert_eq!(Err(OakStatus::ErrPermissionDenied), result); Ok(()) }), @@ -340,8 +348,12 @@ fn create_node_more_confidential_label_ok() { &NodePrivilege::default(), Box::new(move |runtime| { let (_write_handle, read_handle) = runtime.channel_create(&initial_label_clone)?; + let node_configuration = NodeConfiguration { + name: "test".to_string(), + config_type: Some(ConfigType::LogConfig(LogConfiguration {})), + }; let result = - runtime.node_create("log", "unused", &more_confidential_label, read_handle); + runtime.node_create(&node_configuration, &more_confidential_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,