discoenv-oxidize

A set of services for the Discovery Environment written in Rust.

Tooling

You're going to need the following tools to work with the services in this repository.

  • Rust - The Rust programming language.
  • Cargo - Multi-tool for the Rust programming language.
  • sqlx CLI - A command-line tool related to the SQLX crate.
  • protoc - Protocol Buffer compiler.
  • Docker - Needed for building container images.
  • kubectl - Needed for deployments.
  • protoc - Needed for builds.
  • Skaffold - Needed for deployments.

If you're just interested in dealing with the code, then you only need Rust, Cargo, and protoc.

TL;DR (for MacOS and Linux)

  • Install and configure homebrew.
  • Use rustup to install Rust and Cargo.
  • cargo install sqlx-cli
  • brew install protobuf podman skaffold kubectl golang-migrate
  • export DATABASE_URL=postgresql://<user>:<password>@<host>:<port>/de?sslmode=disable
  • cargo build --workspace

Rust & Cargo

You're going to be using Rust and Cargo a lot if you're developing and/or building the projects in this repo, so make sure you have them installed and in your $PATH.

We're using Rust and Cargo 1.68.1 at the time of this writing.

To install Rust and Cargo for your development environment, go to rust-lang.org and follow their instructions.

openssl development libraries

On Ubuntu or Ubuntu derived Linux distributions, you make need to install the libssl-dev package:

sudo apt-get install -y libssl-dev

sqlx CLI

The sqlx library macros query! and query_as! will both use a connection to a database configured with the DATABASE_URI environment variable. You can turn this off either by unsetting that environment variable or by setting the environment variable SQLX_OFFLINE=true.

Connecting to a database during the CI/CD process would be a problem, so you'll need to install the sqlx CLI and use it to serialize query information that can be used during compilation. Documentation for installing the sqlx CLI is available on github.com.

To serialize the queries, do the following:

cargo sqlx prepare --merged

NOTE: The sqlx docs say to use the --workspace flag instead of --merged, but the tool itself said to use --merged. YMMV.

protoc

protoc is the protocol buffer compiler. You're going to need to have it installed and in your path in order to build all of the dependencies for the project.

We're using protoc 3.21.11 at the time of this writing.

For this repository, you just need the protoc compiler, the plugins needed by the p repository are not a requirement.

See the installation instructions here for more information.

Docker

Docker is mainly used to prepare the services for deployment. You don't necessarily need to have it installed if you're just writing code, but if you want to build and deploy the services into a Kubernetes cluster, then you're going to want it (or podman, or buildah).

Just use a reasonably up to date version of Docker. If you're on MacOS, use Docker Desktop.

See docker.com for more information.

On Linux Mint or other Ubuntu derived Linux distributions, you can use podman to create and push images. I recommend using homebrew to install it (yes, on a Linux box):

sudo apt-get install -y uidmap
brew install podman

Information on how to install homebrew on a Linux box is available here: brew.sh.

Skaffold

Skaffold is a tool for building the container images and deploying them into the cluster. If you're not involved with building or deploying the container images, then you don't need it.

Use skaffold version 2.2.0 or later.

For more information on installing Skaffold, see skaffold.dev.

Sources


Repository Organization

You're really going to want to read the Package Layout and Workspaces sections of the Cargo Book to understand what is going on in this repo.

Specifically, the top-level directory is a virtual workspace and there are multiple packages defined in the workspace.

The main goals of this repo's organization are as follows (in no particular order):

  • Safely consolidate as many of the microservices as possible.
  • Provide a common set of modules to make creating new microservices fairly easy.
  • Unify domain object representations across service boundaries.
  • Move as much code into a single repository as possible.
  • Reduce the number of container images needed to deploy a full set of services.
  • Still provide enough flexibility to allow for services that deviate from the common set of libraries and practices.

Directory Structure

  • Cargo.toml - The workspace's Cargo.toml file. Lists which directories are members of the workspace.
  • skaffold.yaml - The skaffold YAML file for building and deploying container images into k8s.
  • discoenv/ - The discoenv crate containing the services and shared libraries deployed from this repo.
  • k8s/ - Container image build and deployment information.

Workspace

The top-level directory of the repository is a Cargo workspace. This allows us to provide multiple Cargo crates from a single repository. The intention is not for every microservice to have its own crate; instead, services should go into the discoenv crate inside the workspace. If a service absolutely needs to exist outside of the discoenv crate, then it can still reside inside this workspace as a new crate.

Discoenv Crate

The discoenv crate is where you should put new microservice code by default. If it's relatively simplistic code that provides access to information in the database as JSON, then consider just adding the functionality to the default binary, discoenv. If the code is a bit more complicated and would benefit from being able to scale separately from the rest of the services, then put it into a secondary binary in the discoenv crate.

The primary binary for the discoenv crate is defined in discoenv/src/main.rs and is a service that provides access to relatively simple HTTP/JSON code that access the database and return JSON encoded information with minimal processing.

The discoenv/src/lib.rs file exposes modules that are provided by the discoenv library. They can be reused across binaries (a.k.a services) contained within the discoenv crate.

Sources


Building and Deploying

There are two things that need to be built from this repository: binaries and container images. During development, you'll be building binaries pretty regularly. Container images are built if and when you're ready to deploy into a cluster.

Cargo

Cargo is provided by rustup and is used to build the Rust services. From the top-level directory run the following to build everything:

cargo build --workspace

If you want to build release versions of the binaries, do the following:

cargo build --workspace --release

The binaries will land in the top-level target directory. That directory should not be checked in.

Docker

If you want to build the container images locally, there are two images to build: rust-builder and disconev-oxidize.

rust-builder is used to build and cache the dependencies so you don't have to do a full rebuild every time.

discoenv-oxidize contains all of the microservice binaries. The Dockerfile for it is multi-stage.

Hopefully you won't need to do this manually locally unless you're working on the images.

First, build rust-builder:

docker build -t harbor.cyverse.org/de/rust-builder .

Then build the image:

docker build -t harbor.cyverse.org/de/discoenv-oxidize .

Skaffold

Skaffold is used to automate building and deploying the images. The k8s resource manifests are located in the k8s directory.

Do not run these commands locally if your target environment's processor architecture is different from your local architecture. In other words, don't run this on an M1 or M2 Mac if you're deploying on an x86_64 Linux box.

Build and push the images.

skaffold build --file-output build.json

Deploy the built images.

skaffold deploy -a build.json

Development

Self-signed certs

Use openssl to generate self-signed certs for development:

openssl genrsa -out key.pem
openssl req -new -x509 -key key.pem -out cert.pem -days 1095
sudo mkdir -p /etc/cyverse/de/tls/
sudo mv key.pem cert.pem /etc/cyverse/de/tls/
sudo chown 0644 /etc/cyverse/de/tls/key.pem /etc/cyverse/de/tls/cert.pem

Local config

Your local config can look something like this. Replace the database connection information as needed.

db:
  uri: postgresql://<db_user>:<db_password>@localhost:5432/<db_name>?sslmode=disable
users:
  domain: "@iplantcollaborative.org"
oauth:
  uri: <keycloak URL>
  realm: <realm>
  client_id: <client-id>
  client_secret: <client-secret>
  entitlements:
    admin: dev
  

cargo watch

Run cargo install to install cargo-watch:

cargo install cargo-watch

Run cargo watch to run the application, recompiling and restarting it on every file change:

cargo watch -x 'run -- --config ./test-config.yaml'  

Or, without TLS:

cargo watch -x 'run --config ./test-config.yaml --no-tls'