1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! A client for the [app-lb](https://github.com/sarocu/app-lb) admin API.
//!
//! app-lb is a load balancer for heyvm Firecracker/KVM microVMs. This crate
//! drives it: register deployments, scale pools, run commands inside a VM, and
//! attach an interactive shell.
//!
//! It is also the library behind the `heyctl` CLI, which is the point — the
//! CLI is this crate's first consumer, so a field the client stops understanding
//! becomes a compile error rather than a silently blank column at somebody's
//! terminal.
//!
//! ```no_run
//! # async fn f() -> hws::Result<()> {
//! use hws::{Client, ExecRequest};
//!
//! let lb = Client::builder("127.0.0.1:9090")
//! .token(std::env::var("APP_LB_TOKEN").unwrap())
//! .build()?;
//!
//! let out = lb.exec("sb-7f3a9c", &ExecRequest::new("uname -a")).await?;
//! println!("{}", out.stdout);
//! # Ok(()) }
//! ```
//!
//! # Authentication
//!
//! Prefer an **app-token**: scoped to particular deployments, revocable without
//! a restart, and optionally expiring. Basic auth also works and is unscoped —
//! it is the operator credential, and the one that mints tokens.
//!
//! ```no_run
//! # async fn f() -> hws::Result<()> {
//! use hws::{AdminScope, Client, NewToken};
//!
//! let admin = Client::builder("127.0.0.1:9090").basic("admin", "s3cret").build()?;
//! let minted = admin.mint_token(
//! &NewToken::new("agent-runner")
//! .admin(AdminScope::Admin)
//! .for_deployments(["sb-7f3a9c"]),
//! ).await?;
//!
//! // The secret is in the reply and nowhere else, ever.
//! println!("{}", minted.token);
//! # Ok(()) }
//! ```
//!
//! A token scoped to specific deployments is refused the fleet-wide routes —
//! including minting — so it cannot widen itself.
//!
//! # Blocking callers
//!
//! Everything here is async. Under the `blocking` feature,
//! [`blocking::Client`] is the same surface with the `await`s taken out.
//!
//! # What this crate does *not* smooth over
//!
//! Three behaviours of app-lb are surprising enough that hiding them would be
//! worse than surfacing them:
//!
//! - A non-zero exit code from [`Client::exec`] is `Ok`, not `Err`. The command
//! ran; it failed.
//! - `exec`'s timeout does not kill anything. It bounds app-lb's call to the
//! daemon — on expiry you get [`Error::Upstream`] and the command **keeps
//! running in the guest**.
//! - A shell socket has no resume. If it drops, the session is gone;
//! reconnecting gives a *new* shell. See [`shell`].
pub use ;
/// The namespace an object belongs to when nothing says otherwise.
///
/// app-lb's own default, repeated here because the item routes for
/// namespace-scoped objects — an auth provider is `(namespace, name)` — need a
/// namespace in the *path*, so a client that omits `--namespace` still has to
/// name one.
pub const DEFAULT_NAMESPACE: &str = "default";
pub use ;
pub use ;
pub use ;
pub use *;
pub use ;
// -- CLI internals ----------------------------------------------------------
//
// Public only so `main.rs` can reach them across the lib/bin boundary. Not part
// of the supported surface, and `doc(hidden)` so they do not appear as though
// they were.
/// Read-modify-write helpers over a deployment spec as a `serde_json::Value`.
///
/// Public because they are genuinely useful to a caller assembling a spec, and
/// because writes go through `Value` by design — see [`api`].