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
122
123
124
125
126
127
128
129
130
131
132
133
134
//! The Heyo Web Services SDK: a client for the
//! [app-lb](https://github.com/Heyo-Computer/hws) admin API.
//!
//! app-lb is a load balancer for heyvm Firecracker/KVM microVMs, and the door
//! to everything a Heyo namespace runs. This crate drives it: create and
//! replace workloads ([`Client::create_deployment`], [`Client::start_rollout`]),
//! scale pools, build and pull their images, run commands inside a VM, attach
//! an interactive shell, and read their metrics and logs ([`Client::obs`]).
//!
//! Every call works with a namespace-scoped token — the credential a tenant is
//! handed — and is narrowed by the server to that namespace.
//!
//! 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 ;
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`].