Skip to content

Commit

Permalink
Move node configuration to runtime (#917)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tiziano88 authored May 26, 2020
1 parent e075a5a commit 3463024
Show file tree
Hide file tree
Showing 80 changed files with 802 additions and 1,607 deletions.
69 changes: 5 additions & 64 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 12 additions & 11 deletions docs/abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 6 additions & 5 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 41 additions & 45 deletions docs/programming-oak.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
```
Expand Down Expand Up @@ -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);
}
```
Expand Down Expand Up @@ -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
Expand All @@ -228,37 +226,21 @@ be serialized into a binary file. The Application first needs to specify a
template configuration file:

<!-- prettier-ignore-start -->
[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: "<bytes>"
}
}
node_configs {
name: "translator"
wasm_config {
module_bytes: "<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"
```
<!-- prettier-ignore-end -->

The `module_bytes: "<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:

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
});
```
Expand All @@ -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.
<!-- prettier-ignore-start -->
[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");
```
<!-- prettier-ignore-end -->
3 changes: 0 additions & 3 deletions examples/abitest/abitest_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
4 changes: 2 additions & 2 deletions examples/abitest/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
Loading

0 comments on commit 3463024

Please sign in to comment.