Backend Rust Axum Quickstarter (be-rust-axum)

The project supports generation of Rust programming language based projects, with the Axum web framework by default, and quick installation and integration within OpenShift Jenkins CICD pipelines within the [OpenDevStack](https://www.opendevstack.org/) context.

For Database/Postgres integration it is recommended to use SQLx. Check the Axum with SQLx example. Enjoy a full async, safe and high performant setup!

Purpose of this quickstarter

This is a Rust project with a common Rust project folder and files structure, with its main.rs file for the final binary to be built, and that makes use of the lib.rs file, which exposes the crates (AKA modules or packages) of the project (where the business logic happens). Similar to Python project structures.

The quickstarter comes with a simple API-server example written in Rust and using the Axum web framework. The package allows to easily build a Rust project, using different Rust crates (packages). It contains the basic setup for Docker, Jenkins, SonarQube and OpenShift.

NOTE The project can be also extended to build other types of solutions with, for example, WASM or IoT.

What files / architecture is generated?

β”œβ”€β”€ πŸ“‚ .config - The local Rust project config folder
β”‚   └── πŸ“„ nextest.toml - The local Nextest config file (required for Jenkins CICD)
β”œβ”€β”€ πŸ“‚ chart - The Helm chart folder
β”‚   β”œβ”€β”€ πŸ“‚ templates - The resource files to define in your project (i.e.: deployment.yml, service.yml,...)
β”‚   β”‚   β”œβ”€β”€ πŸ“‚ tests - Helm tests folder
β”‚   β”‚   β”‚   └── πŸ“„ test-connection.yaml - Helm test connection to app service after a new release
β”‚   β”‚   β”œβ”€β”€ πŸ“„ _helpers.tpl - Helm helpers template
β”‚   β”‚   β”œβ”€β”€ πŸ“„ deployment.yaml - The k8s Deployment template for the app to release
β”‚   β”‚   β”œβ”€β”€ πŸ“„ NOTES.txt - The release notes processed on each release
β”‚   β”‚   └── πŸ“„ service.yaml - The k8s Service template for the app to release
β”‚   β”œβ”€β”€ πŸ“„ .helmignore - The Helm ignore file
β”‚   β”œβ”€β”€ πŸ“„ Chart.yaml - The Helm Chart main config file
β”‚   └── πŸ“„ values.yaml - The values to process on your Helm chart
β”œβ”€β”€ πŸ“‚ docker - The docker context to build
β”‚   └── πŸ“„ Dockerfile - The docker file to deploy and run
β”œβ”€β”€ πŸ€– Jenkinsfile - This file contains Jenkins build configuration settings
β”œβ”€β”€ πŸ“‚ src
β”‚   β”œβ”€β”€ πŸ“‚ api
β”‚   β”‚   β”œβ”€β”€ πŸ“‚ routes
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ¦€ mod.rs - The routes module file
β”‚   β”‚   β”‚   └── πŸ¦€ status.rs
β”‚   β”‚   β”œβ”€β”€ πŸ¦€ mod.rs - The api module file
β”‚   β”‚   └── πŸ¦€ router.rs - The router API routes file
β”‚   β”œβ”€β”€ πŸ“‚ config
β”‚   β”‚   β”œβ”€β”€ πŸ¦€ mod.rs - The config module file
β”‚   β”‚   └── πŸ¦€ settings.rs - The settings file
β”‚   β”œβ”€β”€ πŸ“‚ models
β”‚   β”‚   β”œβ”€β”€ πŸ¦€ mod.rs - The models module file
β”‚   β”‚   └── πŸ¦€ status.rs - The status model example file
β”‚   β”œβ”€β”€ πŸ¦€ lib.rs - The component's library exposing this project's crates to the main.rs
β”‚   └── πŸ¦€ main.rs - The component's binary compilation
β”œβ”€β”€ πŸ“‚ target - The target folder where all builds (debug, release, ...) are stored (do not commit to git!)
β”œβ”€β”€ πŸ“‚ tests - Integration tests folder for all exposed component's crates within the lib.rs
β”‚   β”œβ”€β”€ πŸ¦€ common.rs - Common util implementations and functions
β”‚   └── πŸ¦€ status_test.rs - Testing the status endpoint example
β”œβ”€β”€ πŸ“„ .editorconfig - To share with your team IDEs some files formatting defaults
β”œβ”€β”€ πŸ“„ .gitignore - The Git ignore file, with some Rust defaults
β”œβ”€β”€ πŸ“„ .pre-commit-config.yaml - The pre-commit tool config file, prepared for a Rust project.
β”œβ”€β”€ πŸ“„ Cargo.lock - The Rust dependency hash tree of this project
β”œβ”€β”€ πŸ“„ Cargo.toml - The Rust project config file
β”œβ”€β”€ πŸ“„ deny.toml - Cargo Deny TOML configuration based on defaults
β”œβ”€β”€ πŸ“„ metadata.yml - Component metadata
β”œβ”€β”€ πŸ“š README.md - The README file
β”œβ”€β”€ πŸ“„ release-manager.yml - Configuration file for the Release Manager
β”œβ”€β”€ πŸ“„ rustfmt.toml - The Rust formatter configuration file (for cargo fmt)
└── πŸ“„ sonar-project.properties - This file contains SonarQube configuration settings

Usage - how do you start after you provisioned this quickstarter

The project is production ready when deployed in OpenShift.

Rust community and official resources are great to get to it, see learn Rust.

To get Rust ready on your local environment you just require installing rustup (see install Rust)

# Get the Rustup CLI and already install target computer toolchain and latest stable Rust version
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# If you are new to Rust (also check the rustlings TUI)
rustup doc --book

# Run Unit, Integration and Documentation tests in isolated processes on each test
cargo install cargo-nextest  # install nextest!
cargo nextest run

# Cargo format source code
cargo fmt

# Cargo run locally
cargo run

# Cargo run Rust linters
cargo clippy

# Cargo generate documentation
cargo doc [--document-private-items] --open

One can also extend the cargo features by installing cargo extensions like:

# Live reload locally
cargo install cargo-watch  # https://github.com/watchexec/cargo-watch
# Then you can live reload by:
cargo watch -x run

# Audit Cargo.lock against the advisory DB. The [RustSec Advisory Database](https://github.com/rustsec/rustsec) is a repository of security advisories filed against Rust crates published via crates.io.
cargo install cargo-audit
#Β Audit you dependencies on Licenses, Bans, Advisories and Sources, with [cargo-deny](https://github.com/EmbarkStudios/cargo-deny)
cargo install cargo-deny
# ...

NOTE For an extended list of awesome cargo extensions and Rust tools check here and here.

Metadata

The following are typical metadata values that can be used for components based on this quickstarter: Note that the OpenShift resources will be labeled based on this metadata.

name: <the component id (this is the default, if omitted)>
description: "Some microservice implemented in Rust with Axum web framework"
supplier: https://example.com
version: 1.0.1
type: ods
role: backend
runtime: rust
runtimeVersion: 1.83.0

How this quickstarter is built through Jenkins

The Jenkinsfile is provisioned with this quick starter to ease CI/CD process. In Jenkinsfile, there are various stages:

  • Cargo Check - Checks we can compile:

      cargo check --all-targets
  • Cargo Format - Checks code is properly formatted:

      cargo fmt --all -- --check
  • Cargo Clippy - Collection of lints to catch common mistakes and improve your Rust code (output is also used on SonarQube reports):

      cargo clippy --all-features
  • Cargo Deny - Collection of lints to catch dependency graph issues related to licenses, bans, advisories and sources:

      cargo deny --all-features check
  • Cargo Test - Runs nextest (instead of cargo test, see above why) with xUnit test report generation (see .config/nextest.toml) and code coverage reports with LLVM coverage tool:

      cargo nextest run --profile ci
      cargo llvm-cov nextest --profile ci --lcov
  • Build - Builds the release target binary and moves it to the docker folder:

      cargo build --release

include::partials$secret-scanning-with-gitleaks.adoc

Builder agent used

This quickstarter uses Rust Jenkins builder agent.

NOTE: The ODS Jenkins Rust Agent supports Rust versions 1.83.x, and depending on project’s dependencies it can also handle Minor Rust versions above and below the supported one.

See the docker spec and openshift templates here.

Technologies in use

The following main Rust technologies are in use when running this boilerplate:

  • Axum: Web application framework that focuses on ergonomics and modularity.

  • Tokio: Runtime for writing reliable, asynchronous, and slim applications.

  • Tower: Library of modular and reusable components for building robust networking clients and servers.

  • Hyper: A fast and correct HTTP implementation for Rust.

Known limitations

First of all, please, let us know if you find any limitation or issue to comment on, thanks!

Building with OpenSSL crate or using alternatives

Most of the crates out there, that require cryptographic related features, come with openssl crate as a default dependency feature, but one can check the crate’s docs in regards available features and disable openssl and/or default-features and enable provided alternatives, like rustls`.

See some examples of known crates that can be configured to skip requiring OpenSSL C library:

SQLx crate without openssl dependency

With the sqlx crate, one can avoid openssl dependency by enabling the dependency feature tls-rustls which makes use of the crate rustls, like:

[dependencies]
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls", "postgres", "uuid", "json", "chrono", "macros", "migrate" ] }

See SQLx’s TLS features list support, or all SQLx feature flags, for further learning.

Reqwest crate without openssl dependency

With the reqwest crate, one can avoid openssl dependency by enabling the dependency feature rustls-tls, which makes use of the crate rustls and disabling default features, like:

[dependencies]
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }

See reqwest’s features list for further learning.

Using openssl crate

In some cases one might not be able to skip requiring OpenSSL nor LibreSSL C libraries (see issue), but requires compiling them.

To have a lean compilation and shipping experience with Rust, ODS Jenkins Rust agent provides already the dependencies to build OpenSSL from source and statically link them, hence avoiding any mismatch with existing/multiple OS libraries or none (at build or runtime), by enabling statically linking of the dependency within the binary.

The only missing piece required is to enable the vendored feature in the crate in your Cargo.toml, see example:

[dependencies]
openssl = { version = "0.10", features = ["vendored"] }

By doing so, cargo will locally build OpenSSL and statically link the openssl dependencies into the binarie(s) generated, hence avoiding any OS existence nor dependency of the openssl library.

Alternatives to openssl crate

In most of cases, one does not require OpenSSL, indeed it is recommended to use more modern alternatives like: