Skip to content

Commit

Permalink
Container support for freyja_apps (#9)
Browse files Browse the repository at this point in the history
* Add containerization for freyja_apps

* Minor README changes

* update get_uri to use variables rather than consts

* Resolve fmt warnings

* Fix grammar error in comment

* removed syntax ref in Dockerfile

* Updated readme's to be more concise

* updated toolchain to specific version

* Fix whitespace errors

* fixed bad link

* fixed spacing issue
  • Loading branch information
devkelley authored Oct 27, 2023
1 parent 5f1b0f4 commit 758a61d
Show file tree
Hide file tree
Showing 18 changed files with 395 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .freyja/config/grpc_proxy_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"consumer_address": "0.0.0.0:60010"
}
11 changes: 11 additions & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"cloud_connectors/azure/proto-build",
"freyja_adapters/cloud/azure_cloud_connector_adapter",
"freyja_adapters/digital_twin/ibeji_adapter",
"freyja_apps/ibeji_adapter",
"freyja_apps/in_memory",
"freyja_apps/e2e",
]
Expand Down
73 changes: 73 additions & 0 deletions Dockerfile.freyja_apps
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# SPDX-License-Identifier: MIT

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/engine/reference/builder/

################################################################################
# Create a stage for building the application.

ARG RUST_VERSION=1.72.1
FROM docker.io/library/rust:${RUST_VERSION}-slim-bullseye AS build
ARG APP_NAME=freyja-in-memory-app
WORKDIR /sdv

COPY ./ .

# Add Build dependencies.
RUN apt update && apt upgrade -y && apt install -y \
libssl-dev \
pkg-config \
protobuf-compiler

# Check that APP_NAME argument is valid.
RUN sanitized=$(echo "${APP_NAME}" | tr -dc '^[a-zA-Z_0-9-]+$'); \
[ "$sanitized" = "${APP_NAME}" ] || { \
echo "ARG 'APP_NAME' is invalid. APP_NAME='${APP_NAME}' sanitized='${sanitized}'"; \
exit 1; \
}

# Build the application with the 'containerize' feature.
RUN cargo build --release -p "${APP_NAME}" --features containerize

# Copy the built application to working directory.
RUN cp ./target/release/"${APP_NAME}" /sdv/service

################################################################################
# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the build stage where the necessary files are copied from the build
# stage.
#
# The example below uses the debian bullseye image as the foundation for running the app.
# By specifying the "bullseye-slim" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile. If
# reproducability is important, consider using a digest
# (e.g., debian@sha256:ac707220fbd7b67fc19b112cee8170b41a9e97f703f588b2cdbbcdcecdd8af57).
FROM docker.io/library/debian:bullseye-slim AS final

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser

WORKDIR /sdv

# Copy the executable from the "build" stage.
COPY --from=build /sdv/service /sdv/
COPY --from=build /sdv/target/release/build/ /sdv/target/release/build/

ENV FREYJA_HOME=/sdv/.freyja

# What the container should run when it is started.
CMD ["/sdv/service"]
30 changes: 30 additions & 0 deletions container/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Container Examples

This document describes how to utilize the provided Dockerfiles for containerization.

## Dockerfile Selection

### Freyja Apps

To containerize the [Example Freyja Apps](../freyja_apps/), use
[Dockerfile.freyja_apps](../Dockerfile.freyja_apps). This dockerfile defaults to the
[In Memory Example Application](../freyja_apps/in_memory/).

Currently the following example applications can be containerized:

- [In Memory Example Application](../freyja_apps/in_memory/)
- [Ibeji Adapter Example Application](../freyja_apps/ibeji_adapter)

Each supported application has a README describing the steps to containerize the application.

### Freyja Cloud Connectors

Coming soon!

### Env Files

To run an application in Docker, ensure that the [docker.env](./config/docker.env) is passed in
when running the container.

To run an application in Podman, ensure that the [podman.env](./config/podman.env) is passed in
when running the container.
9 changes: 9 additions & 0 deletions container/config/docker.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# SPDX-License-Identifier: MIT

# DNS name used by the container to communicate with host.
HOST_GATEWAY=host.docker.internal

# Alias for localhost to be replaced by HOST_GATEWAY if run in a container.
LOCALHOST_ALIAS=0.0.0.0
9 changes: 9 additions & 0 deletions container/config/podman.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# SPDX-License-Identifier: MIT

# DNS name used by the container to communicate with host.
HOST_GATEWAY=host.containers.internal

# Alias for localhost to be replaced by HOST_GATEWAY if run in a container.
LOCALHOST_ALIAS=0.0.0.0
5 changes: 4 additions & 1 deletion freyja_adapters/digital_twin/ibeji_adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ tonic = { workspace = true }
tower = { workspace = true }

[build-dependencies]
freyja-build-common = { workspace = true }
freyja-build-common = { workspace = true }

[features]
containerize = []
25 changes: 25 additions & 0 deletions freyja_adapters/digital_twin/ibeji_adapter/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,32 @@
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

#![allow(unused_imports)]

use serde::{Deserialize, Serialize};
use std::env;

/// If feature 'containerize' is set, it will modify a localhost uri to point to container's
/// localhost DNS alias. Otherwise, returns the uri as a String.
///
/// # Arguments
/// * `uri` - The uri to potentially modify.
pub fn get_uri(uri: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "containerize")]
let uri = {
// Container env variable names.
let host_gateway_env_var: &str = "HOST_GATEWAY";
let host_alias_env_var: &str = "LOCALHOST_ALIAS";

// Return an error if container env variables are not set.
let host_gateway = env::var(host_gateway_env_var)?;
let host_alias = env::var(host_alias_env_var)?;

uri.replace(&host_alias, &host_gateway)
};

Ok(uri.to_string())
}

/// Configuration for the Ibeji Adapter.
/// Supports two different schemas based on the service discovery method.
Expand Down
21 changes: 14 additions & 7 deletions freyja_adapters/digital_twin/ibeji_adapter/src/ibeji_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use freyja_contracts::{
entity::Entity,
};

use crate::config::{ChariottDiscoverRequest, Config};
use crate::config::{self, ChariottDiscoverRequest, Config};

const CONFIG_FILE_STEM: &str = "ibeji_adapter_config";
const GET_OPERATION: &str = "Get";
Expand All @@ -43,10 +43,12 @@ impl IbejiAdapter {
chariott_service_discovery_uri: &str,
chariott_discovery_request: ChariottDiscoverRequest,
) -> Result<String, DigitalTwinAdapterError> {
let mut service_registry_client =
ServiceRegistryClient::connect(String::from(chariott_service_discovery_uri))
.await
.map_err(DigitalTwinAdapterError::communication)?;
let chariott_uri =
config::get_uri(chariott_service_discovery_uri).map_err(DigitalTwinAdapterError::io)?;

let mut service_registry_client = ServiceRegistryClient::connect(chariott_uri)
.await
.map_err(DigitalTwinAdapterError::communication)?;

let discover_request = Request::new(DiscoverRequest {
namespace: chariott_discovery_request.namespace,
Expand Down Expand Up @@ -120,11 +122,14 @@ impl DigitalTwinAdapter for IbejiAdapter {
}
};

let invehicle_digital_twin_uri = config::get_uri(&invehicle_digital_twin_service_uri)
.map_err(DigitalTwinAdapterError::io)?;

let client = futures::executor::block_on(async {
execute_with_retry(
max_retries,
Duration::from_millis(retry_interval_ms),
|| InvehicleDigitalTwinClient::connect(invehicle_digital_twin_service_uri.clone()),
|| InvehicleDigitalTwinClient::connect(invehicle_digital_twin_uri.clone()),
Some(String::from("Connection retry for connecting to Ibeji")),
)
.await
Expand Down Expand Up @@ -193,12 +198,14 @@ impl DigitalTwinAdapter for IbejiAdapter {
String::from(GET_OPERATION)
};

let entity_uri = config::get_uri(&endpoint.uri).map_err(DigitalTwinAdapterError::io)?;

let entity = Entity {
id: entity_id,
description: Some(entity_access_info.description),
name: Some(entity_access_info.name),
operation,
uri: endpoint.uri,
uri: entity_uri,
protocol: endpoint.protocol,
};

Expand Down
2 changes: 1 addition & 1 deletion freyja_apps/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ To build and run the application, follow these steps:

cargo run -p freyja-e2e-app

This will rebuild the application as necessary and then run it. Note that running Cargo commands without specifying the `-p` argument might target every package in the workspace! When working with this repository it's recommended to use the `-p` argument with Cargo commands.
This will rebuild the `freyja-e2e-app` application as necessary and then run it.
23 changes: 23 additions & 0 deletions freyja_apps/ibeji_adapter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# SPDX-License-Identifier: MIT

[package]
name = "freyja-ibeji-adapter-app"
version = "0.1.0"
edition = "2021"
license = "MIT"

[dependencies]
# These two dependencies are required for anyone implementing a Freyja application
freyja = { workspace = true }
tokio = { workspace = true, features = ["macros"] }

# Put any dependencies that you need for your adapters down here.
# This samples utilizes the in-memory mock adapters.
in-memory-mock-cloud-adapter = { workspace = true }
ibeji-adapter = { workspace = true }
in-memory-mock-mapping-client = { workspace = true }

[features]
containerize = ["ibeji-adapter/containerize"]
102 changes: 102 additions & 0 deletions freyja_apps/ibeji_adapter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Ibeji Adapter Freyja Example Application

This Freyja Example Application utilizes the [Ibeji Digital Twin Adapter](../../freyja_adapters/digital_twin/ibeji_adapter/) and an in-memory mock cloud connector adapter to show a minimal connected local example of how to retrieve data from the vehicle.

## Build and Run

To build and run the application, follow these steps:

1. (Optional) If necessary, author configuration overrides for the [`InMemoryMockMappingClient`](https://github.com/eclipse-ibeji/freyja/tree/main/mapping_clients/in_memory_mock_mapping_client). Refer to the adapter README files for instructions on how to do this. This repository provides overrides in the [`.freyja`](../../.freyja/) directory that can be used with the [`mixed` sample provided by Ibeji](https://github.com/eclipse-ibeji/ibeji/tree/main/samples/mixed).

1. Set the `$FREYJA_HOME` environment variable. If you are using the provided overrides, you can run the following command to set the variable:

export FREYJA_HOME={path-to-repo-root}/.freyja

Alternatively, you can set the variable in a [Cargo configuration file](https://doc.rust-lang.org/cargo/reference/config.html) to only enable the variable while running a Cargo command.

1. Run the following from the repo root:

cargo run -p freyja-ibeji-adapter-app

This will rebuild the `freyja-ibeji-adapter-app` application as necessary and then run it.

## Containerize the Ibeji Adapter Freyja Example Application

To build and run the application in a container, follow the steps under [Docker](#docker) or
[Podman](#podman). Ensure that the `$FREYJA_HOME` environment variable is set.

### Docker

#### Prerequisites

[Install Docker](https://docs.docker.com/engine/install/)

#### Running in Docker

To run the service in a Docker container:

1. Run the following command in the project's root directory to build the docker container from the
Dockerfile:

```shell
docker build -t freyja_ibeji_adapter --build-arg APP_NAME=freyja-ibeji-adapter-app -f Dockerfile.freyja_apps .
```

The `APP_NAME` build arg needs to be set as `Dockerfile.freyja_apps` defaults to the
[In Memory Example Application](../in_memory/).

1. Once the container has been built, start the container in interactive mode with the following
command in the project's root directory:

```shell
docker run -v ${FREYJA_HOME}:/sdv/.freyja --name freyja_ibeji_adapter -p 60010:60010 --env-file=./container/config/docker.env --add-host=host.docker.internal:host-gateway -it --rm freyja_ibeji_adapter
```

`-v` mounts the `$FREYJA_HOME` path set above in the container allowing the application to use
the provided overrides for the mixed sample in Ibeji.

1. To detach from the container, enter:

<kbd>Ctrl</kbd> + <kbd>p</kbd>, <kbd>Ctrl</kbd> + <kbd>q</kbd>

1. To stop the container, enter:

```shell
docker stop freyja_ibeji_adapter
```

### Podman

#### Prerequisites

[Install Podman](https://podman.io/docs/installation)

#### Running in Podman

To run the service in a Podman container:

1. Run the following command in the project's root directory to build the podman container from the
Dockerfile:
```shell
podman build -t freyja_ibeji_adapter --build-arg=APP_NAME=freyja-ibeji-adapter-app -f Dockerfile.freyja_apps .
```
The `APP_NAME` build arg needs to be set as `Dockerfile.freyja_apps` defaults to the
[In Memory Example Application](../in_memory/).
1. Once the container has been built, start the container with the following command in the
project's root directory:

```shell
podman run --mount=type=bind,src=${FREYJA_HOME},dst=/sdv/.freyja,ro=true -p 60010:60010 --env-file=./container/config/podman.env --network=slirp4netns:allow_host_loopback=true localhost/freyja_ibeji_adapter
```

`-v` mounts the `$FREYJA_HOME` path set above in the container allowing the application to use
the provided overrides for the mixed sample in Ibeji.

1. To stop the container, run:

```shell
podman ps -f ancestor=localhost/freyja_ibeji_adapter:latest --format="{{.Names}}" | xargs podman stop
```
Loading

0 comments on commit 758a61d

Please sign in to comment.