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 configuration to the GRPC proxy #53

Merged
merged 7 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions common/src/config_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ const DOT_FREYJA_DIR: &str = ".freyja";
const FREYJA_HOME: &str = "FREYJA_HOME";

/// Read config from layered configuration files.
/// Uses `{config_file_name}.default.{config_file_ext}` as the base configuration,
/// then searches for overrides named `{config_file_name}.{config_file_ext}` in the current directory and `$FREYJA_HOME`.
/// Uses `{config_file_stem}.default.{config_file_ext}` as the base configuration,
/// then searches for overrides named `{config_file_stem}.{config_file_ext}` in the current directory and `$FREYJA_HOME`.
/// If `$FREYJA_HOME` is not set, it defaults to `$HOME/.freyja`.
///
/// # Arguments
/// - `config_file_name`: The config file name without an extension. This is used to construct the file names to search for
/// - `config_file_stem`: The config file name without an extension. This is used to construct the file names to search for
/// - `config_file_ext`: The config file extension. This is used to construct the file names to search for
/// - `default_config_path`: The path to the directory containing the default configuration
/// - `io_error_handler`: The error handler for `std::io::Error` errors
/// - `config_error_handler`: The error handler for errors from the config library
pub fn read_from_files<TConfig, TError, TPath, TIoErrorHandler, TConfigErrorHandler>(
config_file_name: &str,
config_file_stem: &str,
config_file_ext: &str,
default_config_path: TPath,
io_error_handler: TIoErrorHandler,
Expand All @@ -36,10 +36,10 @@ where
TIoErrorHandler: Fn(std::io::Error) -> TError,
TConfigErrorHandler: Fn(ConfigError) -> TError,
{
let default_config_filename = format!("{config_file_name}.default.{config_file_ext}");
let default_config_filename = format!("{config_file_stem}.default.{config_file_ext}");
let default_config_file = default_config_path.as_ref().join(default_config_filename);

let overrides_filename = format!("{config_file_name}.{config_file_ext}");
let overrides_filename = format!("{config_file_stem}.{config_file_ext}");

// The path below resolves to {current_dir}/{overrides_filename}
let current_dir_config_path = env::current_dir()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use crate::config::Config;
use freyja_common::{config_utils, out_dir};
use freyja_contracts::mapping_client::*;

const CONFIG_FILE: &str = "mock_mapping_config";
const CONFIG_EXT: &str = "json";
const CONFIG_FILE_STEM: &str = "mock_mapping_config";
const CONFIG_FILE_EXT: &str = "json";

/// Mocks a mapping provider in memory
pub struct InMemoryMockMappingClient {
Expand Down Expand Up @@ -41,8 +41,8 @@ impl MappingClient for InMemoryMockMappingClient {
/// Creates a new instance of an InMemoryMockMappingClient with default settings
fn create_new() -> Result<Self, MappingClientError> {
let config = config_utils::read_from_files(
CONFIG_FILE,
CONFIG_EXT,
CONFIG_FILE_STEM,
CONFIG_FILE_EXT,
out_dir!(),
MappingClientError::io,
MappingClientError::deserialize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::config::Config;
use freyja_common::{config_utils, out_dir, retry_utils::execute_with_retry};
use freyja_contracts::mapping_client::*;

const CONFIG_FILE: &str = "mock_mapping_client_config";
const CONFIG_EXT: &str = "json";
const CONFIG_FILE_STEM: &str = "mock_mapping_client_config";
const CONFIG_FILE_EXT: &str = "json";

/// Mocks a mapping provider in memory
pub struct MockMappingServiceClient {
Expand Down Expand Up @@ -49,8 +49,8 @@ impl MappingClient for MockMappingServiceClient {
/// Creates a new instance of a MockMappingServiceClient with default settings
fn create_new() -> Result<Self, MappingClientError> {
let config = config_utils::read_from_files(
CONFIG_FILE,
CONFIG_EXT,
CONFIG_FILE_STEM,
CONFIG_FILE_EXT,
out_dir!(),
MappingClientError::io,
MappingClientError::deserialize,
Expand Down
8 changes: 4 additions & 4 deletions mocks/mock_mapping_service/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use freyja_contracts::mapping_client::{
CheckForWorkResponse, GetMappingResponse, SendInventoryRequest, SendInventoryResponse,
};

const CONFIG_FILE: &str = "mock_mapping_config";
const CONFIG_EXT: &str = "json";
const CONFIG_FILE_STEM: &str = "mock_mapping_config";
const CONFIG_FILE_EXT: &str = "json";

struct MappingState {
count: u8,
Expand All @@ -52,8 +52,8 @@ async fn main() {
.init();

let config = config_utils::read_from_files(
CONFIG_FILE,
CONFIG_EXT,
CONFIG_FILE_STEM,
CONFIG_FILE_EXT,
out_dir!(),
|e| log::error!("{}", e),
|e| log::error!("{}", e),
Expand Down
8 changes: 7 additions & 1 deletion provider_proxies/grpc/v1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ license = "MIT"
[dependencies]
async-trait = { workspace = true }
crossbeam = { workspace = true }
freyja-common = { workspace = true }
freyja-contracts = { workspace = true }
futures = { workspace = true }
log = { workspace = true }
proc-macros = { workspace = true }
samples-protobuf-data-access = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tonic = { workspace = true }
tower = { workspace = true }
tower = { workspace = true }

[build-dependencies]
freyja-build-common = { workspace = true }
17 changes: 17 additions & 0 deletions provider_proxies/grpc/v1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# GRPC Provider Proxy

The GRPC Provider Proxy interfaces with providers which support GRPC. It acts as a consumer for digital twin providers. This proxy supports the operations `Get` and `Subscribe` as defined for the [Ibeji mixed sample](https://github.com/eclipse-ibeji/ibeji/tree/main/samples/mixed). To use this proxy with other providers, those providers will need to support the same APIs as the provider in the Ibeji mixed sample.

## Configuration

This proxy's default config is located at `res/grpc_proxy_config.default.json` and will be copied to the build output automatically. This proxy supports the following configuration settings:

- `consumer_address`: The address for the proxy's consumer

You can override the default values by defining your own `grpc_proxy_config.json`. The adapter will probe for and unify config in this order, with values near the end of the list taking higher precedence:

- The default config
- A `grpc_proxy_config.json` file in the working directory of the executable (for example, the directory you were in when you ran the `cargo run` command)
- `$FREYJA_HOME/config/grpc_proxy_config.json`. If you have not set a `$FREYJA_HOME` directory, this defaults to:
- Unix: `$HOME/.freyja/config/grpc_proxy_config.json`
- Windows: `%USERPROFILE%\.freyja\config\grpc_proxy_config.json` (note that Windows support is not guaranteed by Freyja or this adapter)
20 changes: 20 additions & 0 deletions provider_proxies/grpc/v1/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

use std::env;

use freyja_build_common::copy_to_build_out_dir;

const RES_DIR_NAME: &str = "res";
const DEFAULT_CONFIG_FILE: &str = "grpc_proxy_config.default.json";

fn main() {
// Current directory of the build script is the package's root directory
let config_path = env::current_dir()
.unwrap()
.join(RES_DIR_NAME)
.join(DEFAULT_CONFIG_FILE);

copy_to_build_out_dir(config_path, DEFAULT_CONFIG_FILE);
}
3 changes: 3 additions & 0 deletions provider_proxies/grpc/v1/res/grpc_proxy_config.default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"consumer_address": "[::1]:60010"
}
12 changes: 12 additions & 0 deletions provider_proxies/grpc/v1/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

use serde::{Deserialize, Serialize};

/// The in-memory mock mapping client's config
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
/// The set of config values
pub consumer_address: String,
}
30 changes: 25 additions & 5 deletions provider_proxies/grpc/v1/src/grpc_provider_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{

use async_trait::async_trait;
use crossbeam::queue::SegQueue;
use freyja_common::{config_utils, out_dir};
use log::info;
use samples_protobuf_data_access::sample_grpc::v1::{
digital_twin_consumer::digital_twin_consumer_server::DigitalTwinConsumerServer,
Expand All @@ -18,16 +19,21 @@ use samples_protobuf_data_access::sample_grpc::v1::{
};
use tonic::transport::{Channel, Server};

use crate::grpc_client_impl::GRPCClientImpl;
use crate::{config::Config, grpc_client_impl::GRPCClientImpl};
use freyja_contracts::provider_proxy::{
OperationKind, ProviderProxy, ProviderProxyError, SignalValue,
};

const CONSUMER_ADDR: &str = "[::1]:60010";
const CONFIG_FILE_STEM: &str = "grpc_proxy_config";
const CONFIG_FILE_EXT: &str = "json";
const SUPPORTED_OPERATIONS: &[OperationKind] = &[OperationKind::Get, OperationKind::Subscribe];

/// Interfaces with providers which support GRPC. Based on the Ibeji mixed sample.
#[derive(Debug)]
pub struct GRPCProviderProxy {
/// The proxy config
config: Config,

/// Client for connecting to a provider
provider_client: DigitalTwinProviderClient<Channel>,

Expand All @@ -52,13 +58,22 @@ impl ProviderProxy for GRPCProviderProxy {
where
Self: Sized,
{
let config = config_utils::read_from_files(
CONFIG_FILE_STEM,
CONFIG_FILE_EXT,
out_dir!(),
ProviderProxyError::io,
ProviderProxyError::deserialize,
)?;

let provider_client = futures::executor::block_on(async {
DigitalTwinProviderClient::connect(String::from(provider_uri))
.await
.map_err(ProviderProxyError::communication)
})?;

Ok(GRPCProviderProxy {
config,
provider_client,
entity_operation_map: Arc::new(Mutex::new(HashMap::new())),
signal_values_queue,
Expand All @@ -70,7 +85,9 @@ impl ProviderProxy for GRPCProviderProxy {
async fn run(&self) -> Result<(), ProviderProxyError> {
info!("Started a GRPCProviderProxy!");

let addr: SocketAddr = CONSUMER_ADDR
let addr: SocketAddr = self
.config
.consumer_address
.parse()
.map_err(ProviderProxyError::parse)
.unwrap();
Expand All @@ -94,7 +111,7 @@ impl ProviderProxy for GRPCProviderProxy {
/// # Arguments
/// - `entity_id`: the entity id that needs a value
async fn send_request_to_provider(&self, entity_id: &str) -> Result<(), ProviderProxyError> {
let consumer_uri = format!("http://{CONSUMER_ADDR}"); // Devskim: ignore DS137138
let consumer_uri = format!("http://{}", self.config.consumer_address); // Devskim: ignore DS137138

let operation_result;
{
Expand Down Expand Up @@ -142,7 +159,7 @@ impl ProviderProxy for GRPCProviderProxy {
.insert(String::from(entity_id), operation.clone());

if *operation == OperationKind::Subscribe {
let consumer_uri = format!("http://{CONSUMER_ADDR}"); // Devskim: ignore DS137138
let consumer_uri = format!("http://{}", self.config.consumer_address); // Devskim: ignore DS137138
let mut client = self.provider_client.clone();
let request = tonic::Request::new(SubscribeRequest {
entity_id: String::from(entity_id),
Expand Down Expand Up @@ -295,6 +312,9 @@ mod grpc_provider_proxy_v1_tests {
let request_future = async {
let client = create_test_grpc_client(bind_path.clone()).await;
let grpc_provider_proxy = GRPCProviderProxy {
config: Config {
consumer_address: "[::1]:60010".to_string(),
wilyle marked this conversation as resolved.
Show resolved Hide resolved
},
provider_client: client,
entity_operation_map: Arc::new(Mutex::new(HashMap::new())),
signal_values_queue: Arc::new(SegQueue::new()),
Expand Down
1 change: 1 addition & 0 deletions provider_proxies/grpc/v1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

mod config;
mod grpc_client_impl;
pub mod grpc_provider_proxy;
Loading