Skip to content
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
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ jobs:

- name: Mutants
run: |
cargo mutants --no-shuffle -vV --in-diff git.diff
cargo mutants -e "crates/sdk-build/*/*.rs" --no-shuffle -vV --in-diff git.diff
5 changes: 5 additions & 0 deletions Cargo.lock

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

89 changes: 68 additions & 21 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
[package]
name = "osc-sdk-rust"
version = "2.0.0-alpha.0"
[workspace]
resolver = "3"
members = ["crates/sdk-build", "crates/sdk"]

[workspace.package]
edition = "2024"
authors = ["Outscale SAS <opensource@outscale.com>"]
license = "BSD-3-Clause"
description = "Outscale API SDK"
repository = "https://github.com/outscale/osc-sdk-rust/"
version = "2.0.0-alpha.0"

[dependencies]
[workspace.dependencies]
# SDK dependencies
chrono = { version = "0.4.43", features = ["serde"] }
futures = "0.3.32"
hmac = "0.12.1"
Expand All @@ -20,24 +23,68 @@ serde_urlencoded = "0.7.1"
sha2 = "0.10.9"
tokio = "1"
tower = { version = "0.5.3", features = ["limit", "retry", "util", "buffer"] }
base64 = "0.22"
reqwest = { version = "^0.13", features = ["json", "http2", "system-proxy", "charset"], default-features = false }

[dependencies.base64]
version = "0.22"

[dependencies.reqwest]
version = "^0.13"
features = ["json", "http2", "system-proxy", "charset"]
default-features = false

[dev-dependencies]
tokio = { version = "1", features = ["full", "tracing"] }
# Dev dependencies
env_logger = "*"
tempfile = "3"
serial_test = "3"

[features]
default = ["default-tls", "osc", "oks"]
native-tls = ["reqwest/native-tls"]
default-tls = ["reqwest/default-tls"]
osc = []
oks = []
# Internal dependencies
osc-sdk-rust-build = { path = "crates/sdk-build" }

# https://emschwartz.me/your-clippy-config-should-be-stricter/
[workspace.lints.clippy]
# Don't Panic - prevent panics from unwraps and unsafe slicing or indexing
string_slice = "warn"
indexing_slicing = "warn"
unwrap_used = "warn"
panic = "warn"
todo = "warn"
unimplemented = "warn"
unreachable = "warn"
get_unwrap = "warn"
unwrap_in_result = "warn"
unchecked_time_subtraction = "warn"
panic_in_result_fn = "warn"

# Don't Fail Silently - prevent dropped futures and swallowed errors
let_underscore_future = "warn"
let_underscore_must_use = "warn"
unused_result_ok = "warn"
map_err_ignore = "warn"
assertions_on_result_states = "warn"

# Don't Do Bad Async Stuff - prevent deadlocks and concurrency bugs
await_holding_lock = "warn"
await_holding_refcell_ref = "warn"
if_let_mutex = "warn" # only relevant on editions before 2024
large_futures = "warn"

# Don't Do Unsafe Things with Memory
mem_forget = "warn"
undocumented_unsafe_blocks = "warn"
multiple_unsafe_ops_per_block = "warn"
unnecessary_safety_doc = "warn"
unnecessary_safety_comment = "warn"

# Don't Do Potentially Incorrect Things with Numbers
float_cmp = "warn"
float_cmp_const = "warn"
lossy_float_literal = "warn"
cast_sign_loss = "warn"
invalid_upcast_comparisons = "warn"

# Don't Do Bad Things That are Easy to Avoid
rc_mutex = "warn"
debug_assert_with_mut_call = "warn"
iter_not_returning_iterator = "warn"
expl_impl_clone_on_copy = "warn"
infallible_try_from = "warn"
dbg_macro = "warn"

# Don't `allow` Your Way Around These Lints - every suppression must be
# a deliberate #[expect(..., reason = "…")] rather than a silent #[allow]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
144 changes: 144 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Outscale SDK for Rust β€” Usage Guide

## Table of Contents

- [Installation](#installation)
- [Feature Flags](#feature-flags)
- [Offline Mode](#offline-mode)
- [How the Generator Pipeline Works](#how-the-generator-pipeline-works)
- [Pre-building the Generator](#pre-building-the-generator)
- [Offline Build with GENERATOR_BIN](#offline-build-with-generator_bin)
- [Offline Build with Nix](#offline-build-with-nix)

---

## Installation

Add the SDK to your `Cargo.toml`:

```toml
[dependencies]
osc-sdk-rust = { git = "https://github.com/outscale/osc-sdk-rust.git", branch = "v2" }
```

**Requirements:**

- Rust stable toolchain (2024 edition)
- Go toolchain (for building the code generator; see [Offline Mode](#offline-mode) for pre-built alternatives)
- Valid OUTSCALE API credentials

---

## Feature Flags

| Feature | Default | Description |
| ------- | ------- | -------------------------------------------- |
| `osc` | Yes | Enables the OSC (compute) API client |
| `oks` | Yes | Enables the OKS (Kubernetes) API client |
| `default-tls` | Yes | Uses reqwest's default Rustls TLS backend |
| `native-tls` | No | Uses OpenSSL via reqwest's native-tls feature |

To enable only the OSC API:

```toml
[dependencies]
osc-sdk-rust = { git = "...", branch = "v2", default-features = false, features = ["osc"] }
```

---

## Offline Mode

By default, building `osc-sdk-rust` requires network access for two reasons:

1. **Rust crate dependencies** β€” fetched from crates.io (solved by `cargo vendor` or a pre-populated `Cargo.lock`)
2. **The code generator binary** β€” the `osc-sdk-rust-build` crate acquires the Go generator at build time

This section covers how to eliminate requirement #2. Once the generator is locally available, you can build the SDK without network access to GitHub or any Go module proxy.

### How the Generator Pipeline Works

The generator is a Go program that reads the OpenAPI specification (`config/{osc,oks}/api.yaml`) and its overlay patches (`config/{osc,oks}/patch.yaml`), then emits Rust code (structs, enums, the `Api` trait, and the `Client` implementation).

### Pre-building the Generator

The simplest offline strategy: build the generator once, then point `GENERATOR_BIN` at it.

```bash
# Build the generator from source
cd hacks/generator
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o ~/bin/osc-sdk-generator .

# Verify
~/bin/osc-sdk-generator --help
```

Now any subsequent `cargo build` of the SDK can use this binary:

```bash
GENERATOR_BIN=$HOME/bin/osc-sdk-generator cargo build
```

The `sdk-build` crate checks `GENERATOR_BIN` first β€” if the file exists, it uses it and skips both the GitHub download and the local `go build` attempts. No network access needed for code generation.

### Offline Build with GENERATOR_BIN

For a fully offline build, combine a pre-built generator with vendored Rust dependencies:

```bash
# Step 1: Pre-build the generator (do once, with network)
cd hacks/generator
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /tmp/osc-sdk-generator .

# Step 2: Vendor Rust dependencies (do once, with network)
cargo vendor
mkdir -p .cargo
cat > .cargo/config.toml <<EOF
[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "vendor"
EOF

# Step 3: Build offline (no network required)
GENERATOR_BIN=/tmp/osc-sdk-generator cargo build --offline
```

The `--offline` flag tells cargo to use only the vendored dependencies. `GENERATOR_BIN` tells `sdk-build` to use the pre-built generator without downloading or compiling anything.

### Offline Build with Nix

The repository includes a `flake.nix` that builds the Go generator as a Nix derivation and wires it into the Rust build, producing fully reproducible example binaries without network access at build time.

```bash
# Build the examples
nix build .#examples

# Run
./result/bin/vm
./result/bin/project
```

See `flake.nix` for the derivation details. The flake also provides a development shell:

```bash
nix develop
# Inside the shell, GENERATOR_BIN is already set:
echo $GENERATOR_BIN
cargo build --examples
```

---

## Examples

```bash
# OSC: list VMs by ID
export OSC_ACCESS_KEY=... OSC_SECRET_KEY=...
cargo run --example vm

# OKS: full project lifecycle (list, create, poll, delete)
export OSC_ACCESS_KEY=... OSC_SECRET_KEY=...
cargo run --example project
```
38 changes: 0 additions & 38 deletions build.rs

This file was deleted.

2 changes: 2 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
4 changes: 2 additions & 2 deletions config/oks/cfg.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
spec: ./config/oks/api.yaml
overlay: ./config/oks/patch.yaml
spec: ./api.yaml
overlay: ./patch.yaml
4 changes: 2 additions & 2 deletions config/osc/cfg.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
spec: ./config/osc/api.yaml
overlay: ./config/osc/patch.yaml
spec: ./api.yaml
overlay: ./patch.yaml
9 changes: 9 additions & 0 deletions crates/sdk-build/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "osc-sdk-rust-build"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
description = "Build-time helper to obtain and run code generator"

Loading
Loading