Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Rust client for Trusted Information Retrieval #1110

Merged
merged 8 commits into from
Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/programming-oak.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ All these steps are implemented as a part of the
The Oak Application is then loaded using the Oak Runner:

```bash
./scripts/run_server -a "${PWD}/config.bin"
./scripts/run_server -f "${PWD}/config.bin"
```

The Oak Runner will launch an [Oak Runtime](concepts.md#oak-runtime), and this
Expand Down
16 changes: 16 additions & 0 deletions examples/Cargo.lock

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

1 change: 1 addition & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"chat/module/rust",
"hello_world/module/rust",
"machine_learning/module/rust",
"trusted_information_retrieval/client/rust",
"trusted_information_retrieval/module/rust",
"private_set_intersection/module/rust",
"running_average/module/rust",
Expand Down
20 changes: 20 additions & 0 deletions examples/trusted_information_retrieval/client/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "trusted_information_retrieval_client"
version = "0.1.0"
authors = ["Ivan Petrov <[email protected]>"]
edition = "2018"
license = "Apache-2.0"

[dependencies]
anyhow = "*"
env_logger = "*"
log = "*"
oak_abi = "=0.1.0"
prost = "*"
structopt = "*"
tokio = { version = "*", features = ["fs", "macros", "sync", "stream"] }
tonic = { version = "*", features = ["tls"] }

[build-dependencies]
oak_utils = "*"
tonic-build = "*"
32 changes: 32 additions & 0 deletions examples/trusted_information_retrieval/client/rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// 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 std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let proto_path = Path::new("../../../../examples/trusted_information_retrieval/proto");
let file_path = proto_path.join("trusted_information_retrieval.proto");

// Tell cargo to rerun this build script if the proto file has changed.
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorerun-if-changedpath
println!("cargo:rerun-if-changed={}", file_path.display());

tonic_build::configure()
.build_client(true)
.build_server(false)
.compile(&[file_path.as_path()], &[proto_path])?;
Ok(())
}
116 changes: 116 additions & 0 deletions examples/trusted_information_retrieval/client/rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//
// 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.
//

//! Client for the Trusted Information Retrieval example.

pub mod proto {
tonic::include_proto!("oak.examples.trusted_information_retrieval");
}

use anyhow::anyhow;
use log::info;
use oak_abi::label::Label;
use prost::Message;
use proto::{
trusted_information_retrieval_client::TrustedInformationRetrievalClient,
ListPointsOfInterestRequest, Location,
};
use structopt::StructOpt;
use tonic::{
metadata::MetadataValue,
transport::{Certificate, Channel, ClientTlsConfig},
Request,
};

#[derive(StructOpt, Clone)]
#[structopt(about = "Trusted Information Retrieval Client")]
pub struct Opt {
#[structopt(
long,
help = "URI of the Oak application to connect to",
default_value = "https://localhost:8080"
)]
uri: String,
#[structopt(
long,
help = "PEM encoded X.509 TLS root certificate file used by gRPC client",
default_value = ""
)]
grpc_client_root_tls_certificate: String,
#[structopt(
long,
help = "Requested location (latitude and longitude separated by space)",
default_value = ""
)]
location: Vec<f32>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let opt = Opt::from_args();

let uri = opt.uri.parse().expect("Error parsing URI");
let root_certificate = tokio::fs::read(&opt.grpc_client_root_tls_certificate)
.await
.expect("Could load certificate file");
if opt.location.len() != 2 {
return Err(anyhow!(
"Incorrect number of coordinates: {} (expected 2)",
&opt.location.len()
));
}
let latitude = opt.location[0];
let longitude = opt.location[1];

info!("Connecting to Oak Application: {:?}", uri);
let tls_config = ClientTlsConfig::new().ca_certificate(Certificate::from_pem(root_certificate));
let channel = Channel::builder(uri)
.tls_config(tls_config)
.connect()
.await
.expect("Could not connect to Oak Application");

let mut label = Vec::new();
Label::public_untrusted()
.encode(&mut label)
.expect("Error encoding label");
let mut client = TrustedInformationRetrievalClient::with_interceptor(
channel,
move |mut request: Request<()>| {
request
.metadata_mut()
.insert_bin("x-oak-label-bin", MetadataValue::from_bytes(label.as_ref()));
Ok(request)
},
);

let request = Request::new(ListPointsOfInterestRequest {
location: Some(Location {
latitude,
longitude,
}),
});
info!("Sending request: {:?}", request);

let response = client
.list_points_of_interest(request)
.await
.expect("Could not receive response");
info!("Received response: {:?}", response);

Ok(())
}
41 changes: 27 additions & 14 deletions scripts/build_example
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@ readonly SCRIPTS_DIR="$(dirname "$0")"
# shellcheck source=scripts/common
source "${SCRIPTS_DIR}/common"

language="rust"
application="rust"
client="cpp"
compilation_mode='fastbuild'
docker_config=''
while getopts "e:l:di:h" opt; do
while getopts "e:a:c:di:h" opt; do
case "${opt}" in
h)
echo -e "Usage: ${0} [-h] [-l rust|cpp] [-i base|logless] [-d] -e EXAMPLE
echo -e "Usage: ${0} [-h] [-a rust|cpp] [-c rust|cpp] [-i base|logless] [-d] -e EXAMPLE

Build the given example Oak Application and client.

Options:
-e Example application name (required)
-l Example application variant:
-a Example application variant:
- rust (used by default)
- cpp
-c Example client variant:
- rust
- cpp (used by default)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This isn't quite selecting client implementation language; it's more selecting client build system: cargo (always Rust underneath) vs Bazel (usually C++ underneath, but could also be Go).

I don't think it worth changing just for pedantry's sake, but it may be relevant for thinking about how the Node.js client would fit in here (cc @juliettepretot )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I was a bit puzzled by this, since the go client is run as a "cpp" client. :)

-d Build C++ code for example using debug mode
-i This flag enables packaging the application into a Docker image,
and specifies the version of the Oak server, used by the application:
Expand All @@ -28,8 +32,10 @@ Options:
exit 0;;
e)
readonly EXAMPLE="${OPTARG}";;
l)
language="${OPTARG}";;
a)
application="${OPTARG}";;
c)
client="${OPTARG}";;
d)
compilation_mode='dbg';;
i)
Expand All @@ -45,7 +51,7 @@ if [[ -z "${EXAMPLE+z}" ]]; then
exit 1
fi

case "${language}" in
case "${application}" in
rust)
for module in examples/"${EXAMPLE}"/module*/rust/Cargo.toml; do
# Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
Expand Down Expand Up @@ -96,11 +102,18 @@ if [[ -n "${docker_config}" ]]; then
fi
fi

bazel_build_flags+=(
'--symlink_prefix=bazel-client-'
"--compilation_mode=${compilation_mode}"
)
case "${client}" in
rust)
cargo build --release "--manifest-path=./examples/${EXAMPLE}/client/rust/Cargo.toml"
;;
cpp)
bazel_build_flags+=(
'--symlink_prefix=bazel-client-'
"--compilation_mode=${compilation_mode}"
)

# Build the client with a different output_base so that we don't lose incremental state.
# See https://docs.bazel.build/versions/master/command-line-reference.html#flag--output_base.
bazel --output_base="${CACHE_DIR}/client" build "${bazel_build_flags[@]}" "//examples/${EXAMPLE}/client:all"
# Build the client with a different output_base so that we don't lose incremental state.
# See https://docs.bazel.build/versions/master/command-line-reference.html#flag--output_base.
bazel --output_base="${CACHE_DIR}/client" build "${bazel_build_flags[@]}" "//examples/${EXAMPLE}/client:all"
;;
esac
44 changes: 26 additions & 18 deletions scripts/run_example
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ readonly SCRIPTS_DIR="$(dirname "$0")"
# shellcheck source=scripts/common
source "${SCRIPTS_DIR}/common"

language="rust"
server="base"
application="rust"
client="cpp"
buildargs=""
serverargs=""
while getopts "s:l:de:vh" opt; do
while getopts "s:a:c:de:vh" opt; do
case "${opt}" in
h)
echo -e "Usage: ${0} [-h] [-s base|logless|none] [-l rust|cpp] [-d] [-v] -e EXAMPLE [-- CLIENT_ARGS]
echo -e "Usage: ${0} [-h] [-s base|logless|none] [-a rust|cpp] [-c rust|cpp] [-d] [-v] -e EXAMPLE [-- CLIENT_ARGS]

Build and run the given example Oak Application and client.

Expand All @@ -22,15 +23,20 @@ Options:
- logless: base version of the server with debug logging compiled out
- none: run an application client without a server
-d Build C++ code for example using debug mode
-l Example application variant:
-a Example application variant:
- rust (used by default)
- cpp
-c Example client variant:
- rust
- cpp (used by default)
-v Enable verbose/debug output for the server
-h Print Help (this message) and exit
Options after -- will be passed to the example client program."
exit 0;;
l)
a)
language="${OPTARG}";;
c)
client="${OPTARG}";;
s)
case "${OPTARG}" in
base|logless|none)
Expand Down Expand Up @@ -59,13 +65,13 @@ fi


if [[ "${server}" != "none" ]]; then
"${SCRIPTS_DIR}/build_example" ${buildargs} -l "${language}" -e "${EXAMPLE}"
"${SCRIPTS_DIR}/build_example" ${buildargs} -a "${application}" -e "${EXAMPLE}"

# Run a server in the background.
# The server is being built before running, so the build process will not happen in the
# background.
"${SCRIPTS_DIR}/build_server" -s "${server}"
"${SCRIPTS_DIR}/run_server" ${serverargs} -s "${server}" -l "${language}" -e "${EXAMPLE}" &
"${SCRIPTS_DIR}/run_server" ${serverargs} -s "${server}" -a "${application}" -e "${EXAMPLE}" &
readonly SERVER_PID=$!
to_kill+=("${SERVER_PID}")

Expand All @@ -76,11 +82,6 @@ if [[ "${EXAMPLE}" == 'abitest' ]]; then
# TODO(#1040): reinstate storage tests when Rust runtime supports them.
# TODO(#953): reinstate gRPC server server-streaming tests when Rust runtime supports them.
readonly ADDITIONAL_ARGS=('--test_exclude=(Storage|GrpcServerServerStreamingMethod)')
elif [[ "${EXAMPLE}" == 'aggregator' ]]; then
readonly ADDITIONAL_ARGS=(
'--bucket=test'
'--data=1:10,2:20,3:30'
)
else
readonly ADDITIONAL_ARGS=()
fi
Expand All @@ -94,12 +95,19 @@ else
readonly TLS_ARGS=("--ca_cert=${SCRIPTS_DIR}/../examples/certs/local/ca.pem")
fi

readonly CLIENT_ARGS=("${@}") # Choose client args provided after '--'.

# Run the application client.
if [ $# -eq 0 ]; then
"./bazel-client-bin/examples/${EXAMPLE}/client/client" "${TLS_ARGS[@]}" "${ADDITIONAL_ARGS[@]-}"
else
readonly CLIENT_ARGS=("${@}") # Choose client args provided after '--'.
"./bazel-client-bin/examples/${EXAMPLE}/client/client" "${TLS_ARGS[@]}" "${ADDITIONAL_ARGS[@]-}" "${CLIENT_ARGS[@]-}"
fi
case "${client}" in
rust)
# Rust example parameters only support `-` as a delimeter, and C++ ones only `_`.
cargo run --release --target=x86_64-unknown-linux-musl --manifest-path="${SCRIPTS_DIR}/../examples/${EXAMPLE}/client/rust/Cargo.toml" -- \
"--grpc-client-root-tls-certificate=${SCRIPTS_DIR}/../examples/certs/local/ca.pem" \
"${CLIENT_ARGS[@]-}"
;;
cpp)
"./bazel-client-bin/examples/${EXAMPLE}/client/client" "${TLS_ARGS[@]}" "${ADDITIONAL_ARGS[@]-}" "${CLIENT_ARGS[@]-}"
;;
esac

kill_bg_pids
Loading