インストール
呼び出しはブロックして Result を返します。オプションはメソッドチェーンの …Options、期間は Duration、時刻は SystemTime で、API の null は Option です。依存関係は 4 つ(serde_json、ring 付きの rustls、webpki-roots、libc)で、非同期ランタイムは不要です。crates.io のクレート名は glac です。
[dependencies]
glac = "0.1"クライアントと製品
use glac::{AppOptions, DriveOptions};
let client = glac::Client::new()?; // GLAC_API_KEY and GLAC_API_URL; another address or key: Client::builder()
let app = client.cube().deploy("ghcr.io/acme/api:1.4", &AppOptions::new().name("api").port(8080))?; // Cube: waits until it serves
println!("{:?}", app.url());
let sbx = client.sandboxes().create()?; // Sandbox
println!("{}", sbx.run("python3 -c 'print(6*7)'")?.stdout);
let drive = client.drives().create_with(&DriveOptions::new().size_bytes(1 << 30))?; // Drive
println!("{}", glac::run("echo hello")?.stdout); // the shorthand: a sandbox for one command, on the client the environment describes各製品はクライアントの名前空間で、クライアントを借用する軽量なハンドルです。
client.cube():キューブ。サービスとして動くイメージで、API ではアプリと呼びます。型には API の名前を使います:App、AppOptions、Version、Instance。client.workers()はその別名で、非推奨です。client.sandboxes():サンドボックスと、そのスナップショット(.snapshots())、それを開く SSH キー(.ssh_keys())。client.drives():ドライブ。サンドボックスより長く残るプロジェクトのディスクで、それぞれ一度に 1 つのサンドボックスが読み書き可能でマウントします(Drive、DriveOptions、DriveMount)。そのスナップショット(DriveSnapshot、DriveSnapshotOptions、DriveForkOptions)。client.volumes():その下にあるボリューム(Volume、VolumeOptions、VolumeMount)。client.backups():ボリュームのバックアップ(Backup、BackupOptions)。コントロールプレーンの機能です。client.domains():プロジェクトの独自ドメイン(Domain、DomainOptions)。アプリやサンドボックスのポートのための名前で、コントロールプレーンの機能です。client.webhooks()とclient.keys():プロジェクトのイベントの送信先(Webhook、WebhookOptions)と API キー(ProjectKey、KeyOptions)。コントロールプレーンの機能です。client.destinations():プロジェクトのアプリのログ、メトリクス、トレースのエクスポート先(Destination、DestinationOptions)。コントロールプレーンの機能です。client.secrets()とclient.variables():プロジェクトのシークレットと共有変数。client.deploys():プロジェクトのデプロイ(/v1/deploys、コントロールプレーンの機能)。複数のサービスを、それぞれが待つものの順にまとめてデプロイし、まとめて切り替えます(プロジェクトのデプロイ)。create([("api", DeployOptions::new().image("ghcr.io/acme/api:2")), ("web", DeployOptions::new())])は、各サービスに単独でのデプロイと同じものを渡してデプロイを待ち、置き換わったデプロイがあればそれを追います。失敗すると、デプロイのコード(job_failed、job_timed_out、job_lostなど)と、param()(services.<name>)としてのサービスを持つ、種類がdeploy_failedのエラーになります。create_with(…, &ProjectDeployOptions::new().no_wait())、get(id)、list()もあります。- どの製品にも属さないものはクライアント自身のものです。アカウント、使用量、プランと請求、プロジェクトとそのイベントです。
キーは GLAC_API_KEY から、アドレスは GLAC_API_URL から読みます。別のアドレス、キー、タイムアウトを使うには、クライアントをビルドします。
let client = Client::builder().api_key(key).base_url(address).build()?;
let named = client.sandboxes().create_with(&CreateOptions::new().name(&name).env("STAGE", "dev").never_sleep())?;**短縮形。**サンドボックスの SDK の 1 行の書き方は、環境変数が示すクライアント(Client::shared()。最初に使うときに作られる)での client.sandboxes() として残っています:glac::run、glac::run_with、Sandbox::create、Sandbox::get、Sandbox::get_or_create、Sandbox::list と、その _with 形と _page 形です。それ以外はすべてクライアントを受け取ります。
オプションを取る呼び出しには、それぞれ 2 つの形があります:create() と create_with(&options)、run(cmd) と run_with(cmd, options)。SDK がラップしていない API は client.request("GET", "/v1/webhooks", &[], None) で呼びます。API の 3 つの名前は Rust のキーワードなので、名前を変えています。エラーの type は kind()、ファイルの type は FileEntry::kind、files/move は rename です。
キューブ
以下、cube は client.cube() です。
| 呼び出し | 内容 |
|---|---|
cube.deploy(image, &AppOptions::new().name("api").port(8080)) |
glac deploy と同じくイメージをデプロイする:その名前のアプリの新しいバージョン、または新しいアプリ。バージョンがトラフィックを受け取るまで待ち、その時点のアプリを返す(url()、current_version()) |
cube.create_with(&AppOptions), cube.get(id_or_name), cube.get_or_create_with(name, &AppOptions), cube.list() |
アプリを作成する(イメージを指定すると、最初のバージョンをデプロイして待つ)。1 つを取得する。取得または作成する。一覧。drives([DriveMount…]) は、唯一のインスタンスが読み書き可能でマウントするドライブ(多数のインスタンスやキーを持つアプリは 409 drive_single_writer で拒否される。バージョンはドライブを読み書き可能でしかマウントせず、スナップショットの読み取り専用のマウントはサンドボックス用)。key_drives([KeyDrive…]) は、キーを使うアプリのキーごとのインスタンスに専用のドライブを持たせる。ドライブは最初のインスタンスとともに作られ、キーとともに削除される |
app.deploy_with(&DeployOptions), app.update(&AppChanges), app.rollback(Some("1"), true), app.versions(), app.delete() |
新しいバージョン。変更。トラフィックをあるバージョンに戻す。バージョンの一覧。削除 |
AppOptions と DeployOptions の .runtime(json!({"ulimits": {"nofile": 65535}})) |
API が受け付けるとおりのバージョンのランタイム設定:ulimits、sysctls、tmpfs、dns、shm_size_bytes。全体が置き換わる |
app.logs(), cube.logs_with(name, &LogsOptions), app.log_usage(…), app.info().logs_state, app.instance(key, None) |
ログ(行のイテレーター。アプリの名前でも可)。ログが数えた量。ログを保持する各マシンが、今そこで出力されるものを長期保存しているかどうか(ログ)。キー別インスタンス |
instance.upgrade(Some("3")), app.upgrade_instance(key, version); up.wait(), up.wait_for(limit); instance.upgrades(), app.instance_upgrades(key), app.instance_upgrade(key, id) |
キーのインスタンスを、アプリの別のバージョンへその場で移す。None はアプリの現在のバージョンで、それ以外は ID か番号(準備ができたもので、古いものも可)。ID、ノード、キー、アドレス、トークン、専用のドライブはそのままで、ディスク、マシン、プロセスが新しいバージョンのものになり、切り替えの間、リクエストはエッジで保留される。進行中の Some(Upgrade) を返す(その info():status は preparing、switching、done、failed。from_version、to_version、timings、error、put_back、kind(upgrade または revert)、when、keep_days、rollback(リバートに必要なもの。keep_days の間保持される)、reverts、batch。API でインスタンスのスリープを待つよう求めたアップグレードは、スリープするまで waiting で、取り消された場合(API の DELETE、またはバッチの停止)は cancelled。取り消されたアップグレードは終了しており、その待機は失敗したものと同じく、コード cancelled でエラーになる)。すでにそのバージョンなら None。wait() は、新しいバージョンが立ち上がらなかった場合、インスタンスを元のバージョンのままにして、種類が upgrade_failed のエラー(コードは upgrade_failed か upgrade_not_here)になる。instance.refresh() はインスタンスを読み直す(version_id) |
app.upgrade_instance_with(key, &UpgradeOptions::new().version("4").when_asleep().keep_days(14)), instance.upgrade_with(&…) |
when_asleep(インスタンスがスリープするまで waiting で待ち、スリープした時点で始めるため、リクエストは 1 つも保留されない)と keep_days(0〜365 日、指定しなければ 7 日。完了後にリバートに必要なものを保持する日数で、スナップショットと同じく課金される)を指定した同じ呼び出し。インスタンスとは別のドライブやボリュームをサービスがマウントするバージョンは、その場でのアップグレードにはならず、upgrade_not_here で failed になる |
up.cancel(), app.cancel_instance_upgrade(key, id); up.revert(keep_days), app.revert_instance_upgrade(key, id, keep_days) |
待機中(インスタンスのスリープ待ち、またはバッチ内の順番待ち)のアップグレードを取り消す:cancelled になり、インスタンスは元のバージョンのまま(始まったものは 409 upgrade_started)。リバート:アップグレードが保持していたもの(ディスク、アップグレード前の状態のドライブ(先に現在の中身のスナップショットを作る)、移行元のバージョン)にインスタンスを戻す。種類が revert の新しい Upgrade で、アップグレードと同じように進み、待てる。戻り先がもう保持されていなければ 409 rollback_gone で拒否される |
app.upgrade_instances(&UpgradeBatchOptions::new().version("4").from_versions(["3"]).keys(["k"]).when_now().keep_days(1).concurrency(8).max_failures(1)); app.upgrade_batches(), app.upgrade_batch(id), app.stop_upgrade_batch(id); batch.wait(), batch.wait_for(limit), batch.refresh(), batch.stop() |
アプリの複数のキーを 1 つのバージョン(指定しなければアプリの現在のバージョン)へまとめてアップグレードするバッチ。それぞれインスタンスがスリープした時点で(when_now なら順番が来た時点で)、指定しなければ同時に 4 つ(1 つのノードでは 2 つ)ずつ行い、指定しなければ 5 回失敗した時点で failed として止まる。info() が UpgradeBatchInfo の UpgradeBatch:status(running、done、failed、stopped)、stop_reason、counts、upgrades(各メンバーのキー、アップグレード、ステータス、エラー)、skipped、origin。wait() はどう終わっても Ok。スリープしないインスタンスのメンバーがあるとバッチは動き続けるため、wait_for は wait_timeout で終わる。stop() は待機中のメンバーを取り消す |
app.open_instance_port(key, "tcp", port, &["2001:db8::/32"]), app.instance_ports(key, "tcp"), app.close_instance_port(key, "tcp", port) |
インスタンスがどこにあっても、キーのインスタンスが持つ専用のパブリック IPv6 アドレスで開いているポート:そのサンドボックスの open_port、ports、close_port を、アプリとキーで指定(UDP なら "udp"。キー別インスタンス)。専用のアドレスのないインスタンスは拒否される(409 no_ipv6) |
AppOptions::checkpoint(n), AppChanges::checkpoint(n), AppChanges::no_checkpoint() |
アプリのキー別インスタンスの稼働中に n 秒ごとに取るチェックポイント(1〜600。0 ならデフォルトの 10 秒ごと)と、その解除。AppInfo::checkpoint が設定を示す。インスタンスの SandboxInfo::checkpoint(Option<SandboxCheckpoint>:every_seconds、last_at、behind_seconds、bytes_per_second、stored_bytes、error)が、チェックポイントの状況を示す |
app.metrics(from, to, step) |
アプリを処理するマシンが計測した値。1 分ごと(Some("hour") なら 1 時間ごと):ステータスのクラス別のリクエスト、所要時間、インスタンスとその使用量(メトリクス)。API のオブジェクトを返す |
AppOptions と AppChanges の .trace_sample_rate(0.1)、.trust_incoming_trace(true)。app.info().traces |
アプリのリクエストの一定割合について作られるプラットフォームのスパンで、インスタンス自身のスパンはその子になる(指定しなければなし)。"traces" を受け付けるプロジェクトのエクスポート先に送られる(トレース)。アプリの設定(AppTraceSettings) |
app.create_key_secret(), app.key_secrets(), app.key_secret(id), app.delete_key_secret(id), glac::sign_key(secret, app_id, key, expires) |
アプリのキーの署名に使うシークレット(KeySecret。secret を持つのは作成直後のものだけ)と、それで署名した、エッジを通るリクエストが運ぶ形のキー(署名付きキー)。glac::path_segment は、それをパスのセグメントとして書く |
既存のアプリへの deploy は、まずオプションのうちアプリ自体のもの(リージョン、スケール、キー、エグレスルール、メタデータ、ログ、トレース、public、depends_on、ネットワーク:PATCH /v1/apps/{id}。どれかを指定した場合のみ)を変更し、次にイメージと残りからバージョンを作ります(POST /v1/apps/{id}/versions)。指定しないものはアプリのものが使われます。app.update(…) の後に app.deploy_with(…) を呼ぶのと同じリクエストです。その名前のアプリがない場合(404 app_not_found)や名前を指定しない場合は、すべてを指定してアプリを作ります(POST /v1/apps)。そこで 409 name_taken になるのは、その間に誰かが作ったということで、その人のアプリにデプロイします。no_wait() はバージョンが作られた時点で戻ります。立ち上がらなかったバージョンは、kind() が deploy_failed で、バージョン自身のコードと出力を持つエラーになります。アプリはコントロールプレーンの機能で、単独で動くノードは、これらの呼び出しすべてに 404 no_such_route を返します。
サンドボックス
以下、sandboxes は client.sandboxes() です。
let mut sbx = client.sandboxes().create()?; // stays until deleted, also when the value is dropped
let res = sbx.run("python3 -c 'print(6*7)'")?; // res.stdout, res.stderr, res.exit_code
println!("{} {}", res.stdout, sbx.url(3000)?);文字列で指定したコマンドは sh -c で実行され、文字列の配列ならプログラムとその引数です。
let cmd = sbx.exec("node server.js")?; // do not wait
for piece in cmd.output() {
print!("{}", String::from_utf8_lossy(&piece?.data));
}| 呼び出し | 内容 |
|---|---|
sandboxes.create_with(&CreateOptions::new().image(…).name(…).env(k, v).drive(DriveMount::new("/data", id)).sleep_after(d).never_sleep().sleep_notice(d).sleep_hold_max(d).egress(…)) |
サンドボックスを作成する。どのオプションも省略可能。起動時に始めるサービス:.start(true)(イメージ自身の起動コマンド)、.start_line(…)、.start_argv(…) を、.ready_tcp(port)、.ready_http(port, path)、.ready_command(…) とともに指定。.hostname(…)、.stop_signal(…)、.stop_grace(d)。.project_network() で最初からプロジェクトのプライベートネットワークにつなぐ(info().project_address)。VolumeMount::new("/data", vol).copy() は、イメージのそのパスにあるものを新しいボリュームに入れる(サンドボックスの作成) |
sbx.info().app_id, version_id, key, image_digest |
コントロールプレーンで作ったサンドボックスはアプリのインスタンスである。イメージを指定せずに作った場合はプロジェクトのデフォルトのサンドボックスアプリ(sbx-sandbox)、それ以外はそのイメージのアプリ(アプリの instances() は、そのアプリのサンドボックスをそれぞれのキーの下に一覧表示する)。サンドボックスが属するそのアプリのバージョン、作成元のイメージ(その後移動したタグはアプリの新しいバージョンになり、それ以降に作ったサンドボックスはそちらに属する)、そこでのキー、名前または ID、ダイジェストで示すイメージ。作成のレスポンスとすべての読み取りで示され、保持される。テンプレートやスナップショットから作ったものと、フォークでは None |
CreateOptions::init("systemd"); SandboxInfo::init |
システム全体:イメージ自身の systemd がサンドボックスの PID 1 になる(イメージを指定しなければ、systemd と OpenSSH 入りのプラットフォームの Ubuntu)。パッケージ、systemctl で起動するサービス、cron、ジャーナルが通常のマシンと同じように動き、専用のマシン ID を持ち、スリープ、復帰、移動を経ても保たれる。起動コマンドや準備完了のチェックとは併用できず(400 invalid_start)、スナップショットからも作れない。init は、systemd なら Some("systemd")、プラットフォームの init なら None |
sandboxes.get, sandboxes.get_or_create, sandboxes.list_with(&ListOptions), sandboxes.list_page(&ListOptions) |
検索。検索または作成。一覧。1 ページ分 |
CreateOptions と UpdateOptions の .suspend_after(d)、.wake_daily_at("03:30")(.default_suspend_after()、.no_daily_wake() で元に戻す)。SandboxInfo::suspend_after、wake_daily_at |
アイドルで停止した後、メモリを保存して本当にスリープするまで一時停止したままでいる時間(1 秒〜1 日。ノードは最大 10 分に抑える)と、休止中に、復帰を待つ自身の処理のために毎日復帰させる時刻(UTC) |
CreateOptions と UpdateOptions の .on_expiry("sleep")、.held_asleep(true)。SandboxInfo::on_expiry、held_asleep |
expires_at の時刻が来たときの動作:サンドボックスを delete(デフォルト)、sleep(スリープさせて保持し、リクエストではいつもどおり復帰)、hold(同じく、スリープのまま保持)。そしてスリープ中にポートへのリクエストで復帰させるかどうか。スリープのまま保持されている間、プレビューアドレスは 502 sandbox_paused を返し、API の呼び出しでは復帰する |
sandboxes.run(cmd), sandboxes.run_with(cmd, RunOptions, &CreateOptions) |
1 つのコマンドのためのサンドボックス:作成、利用、削除 |
sbx.run_with(cmd, RunOptions::new().cwd(…).timeout(d).on_stdout(f)) |
実行して待つ。CommandResult は stdout、stderr、exit_code、duration、usage、wake を持つ |
sbx.exec, sbx.terminal() |
コマンドを開始する(wait()、output()、write()、kill())。ターミナル |
sbx.files() |
read、read_text、write、list、stat、exists、mkdir、rename、remove、そして set_attributes(path, mode, size_bytes, modified_at):ファイルのモード、長さ、最終更新時刻。.as_user("node") |
sbx.url(port), sbx.token_with(scopes, expires_in) |
ポートへのリンク(ローカルで署名)。このサンドボックス用のトークン |
sbx.info().ipv6, sbx.open_port("tcp", port, &[…]), sbx.ports("tcp"), sbx.close_port("tcp", port), .inbound(InboundPort::tcp(port, […])) |
専用のパブリック IPv6 アドレス(ある場合。なければ None)。指定した送信元(IPv6 のプレフィックスかアドレス。全員なら "::/0")に対して開いたポートと、その接続先。開いているポートの一覧。ポートを閉じる(開いていたかどうかを返す)。CreateOptions と UpdateOptions の一覧。受信はデフォルトで閉じており、開いたものはスリープ中も移動しても開いたまま。ネットワークを参照 |
sbx.revoke_token(token) |
そのトークンだけがそれ以降拒否され、ほかのトークンはそれまでどおり使える。トークンそのもの(Token::token)か、その ID(Token::id、tok_…)で指定。1 つのサンドボックスで保持できる、期限の切れていない個別の無効化は 1,000 個まで(超えると 409 too_many_revoked_tokens。その場合は revoke_tokens() を使う)。token_id と expires_at を持つ TokenRevocation を返す |
sbx.revoke_tokens() |
それまでにこのサンドボックス用に発行したトークンがすべて使えなくなる(401 invalid_token)。プレビューリンクや Cookie の中のトークン、SSH セッションが持つトークンも含む。その後に作ったトークンは使える。API キーが必要。TokenRevocation(sandbox_id、generation、revoked_at)を返す |
sbx.update, sbx.sleep(), sbx.restart(), sbx.delete(), sbx.snapshot(), sbx.fork(n), sbx.restore(…) |
変更。スリープ。ディスクからの起動。削除。スナップショット。フォーク。復元 |
CreateOptions::new().checkpoint(10), UpdateOptions::new().checkpoint(10), .no_checkpoint(); info().checkpoint |
稼働中のチェックポイント(1〜600 秒間隔。0 なら 10 秒)。マシンが失われてもプロセスごと再開できる。解除。状況。チェックポイントを取れるマシンがなければ 409 checkpoints_unavailable |
info().disk_writes |
今日(UTC の 1 日)のディスクの書き込み量と、全速で書き込める 1 日の割り当て量(Option<SandboxDiskWrites>):today_bytes(うちチェックポイントのコピーがホストのディスクに書き込んだ量が checkpoint_copy_bytes)、full_speed_until_bytes(割り当て量。ノードに別の指定がなければ 100 GiB。None ならノードは何も減速しないが、数えることは同じ)、limited と limited_until(割り当てを超えると、その日が終わるまでディスクは毎秒 2 MiB で書き込みを受け付け、失敗する書き込みはない)。ディスクをオブジェクトストアにコピーしないノードでは None で、何も数えない。ドライブへの書き込みはここでは数えない(ドライブへの書き込みに料金はかからない) |
sandboxes.snapshots(): get, list, list_with(&ListOptions::new().sandbox(id)) |
スナップショットの再取得。snap.rename(Some(name))(None なら名前なし)、snap.expire_at(Some(t))(None なら削除しない)、snap.delete() |
sandboxes.ssh_keys(): create(public_key), create_with(public_key, &SshKeyOptions), get, list, list_with |
ゲートウェイを通じてサンドボックスを開く SSH キー(ssh <sandbox>@<gateway>)。秘密鍵は、何かを送る前に拒否される |
ドライブ
ドライブは、サンドボックスより長く残るプロジェクトのディスクです。サンドボックスの作成時か実行中に、一度に 1 つのサンドボックスが読み書き可能でマウントし、サンドボックスがなくなると中身ごと保持され、次のサンドボックスがマウントできます。次のサンドボックスは同じリージョンのもの、またはマシンがホームのオブジェクトストアを読める別のリージョンのもの(後述)です。書き込まれるそばからリージョンのオブジェクトストアにコピーされるため、別のノード上のサンドボックスもマウントできます。その場合は先にそこへ運ばれます。課金されるのは書き込まれたものだけです(used_bytes)。ドライブのスナップショットはオブジェクトストア内のドライブのバージョンで、何もコピーされません。フォークはスナップショットから作る新しいドライブ、復元はスナップショットの内容を再びドライブの内容にするもので、いくつものサンドボックスがスナップショットを同時に読み取り専用でマウントできます。ドライブはコントロールプレーンの機能で(/v1/drives)、単独で動くノードに対しては、どの呼び出しもノードの 404 no_such_route になり、そのノードのディスクはボリュームです(後述)。
use glac::DriveMount;
let drive = client.drives().create_with(&DriveOptions::new().size_bytes(1 << 30))?; // Drive
let sbx = client.sandboxes().create_with(&CreateOptions::new().drive(DriveMount::new("/data", drive.id())))?;
sbx.run("echo kept > /data/note")?;
client.drives().detach(sbx.id(), drive.id())?;
let other = client.sandboxes().create()?;
client.drives().attach(other.id(), &DriveMount::new("/data", drive.id()))?;
println!("{}", other.run("cat /data/note")?.stdout); // kept| 呼び出し | 内容 |
|---|---|
drives.create_with(&DriveOptions::new().name(…).size_bytes(…).region(…).durability(…)), drives.get(id_or_name), drives.list(), drives.list_page(&ListOptions) |
ファイルシステム以外は空のドライブを作る。どのオプションも省略可能(size_bytes:4 KiB ブロックの整数倍で最小 16 MiB。指定しなければ 64 GiB、無料プランでは 5 GiB。region:na、eu、ap。durability:standard、またはゲストの fsync が、それ以前のすべての書き込みがオブジェクトストアに届いてから戻る strict)。1 つを取得。新しい順の一覧。1 ページ分 |
drives.rename(id_or_name, Some(name)), drives.delete(id_or_name) |
名前の変更(None なら名前なし)。中身ごと削除。スナップショットは残る |
drives.attach(sandbox, &DriveMount::new(path, drive)), drives.detach(sandbox, drive), drives.force_detach(sandbox, drive) |
既存のサンドボックスに、読み書き可能ですぐにマウントする(スリープ中のサンドボックスは復帰する)。アンマウントする(プロセスが使っている間は force_detach)。どれもドライブを返す。スナップショットを指定したマウント(DriveMount::snapshot)は attach_snapshot で行い、attach は何かを送る前にそれを拒否する。スナップショットなしの mode("snapshot") は API が拒否する(400 missing_field) |
drive.attach(sandbox, path), drive.detach(sandbox), drive.rename(name), drive.refresh(), drive.delete() |
同じことを Drive に対して行う。その info() は DriveInfo:id(vol-…。ドライブはボリュームでもある)、name、size_bytes、used_bytes、attached_to(DriveAttachment:サンドボックス、パス、モード)、region、home、status、lease(DriveLease:唯一の書き込み元のノード、エポック、期限)、app_id と key(キー別インスタンスのもの)、parent_drive_id と root_drive_id(フォーク元のドライブ(その後削除されている場合もある)と、フォークの系統の最初のドライブ。フォークでないドライブでは None)、managed_by(ManagedBy。そのドライブを持つデータベースの id。それ以外では None)、created_at。データベースのドライブを読み書き可能でマウントできるのはデータベースのインスタンスだけで、名前の変更、復元、削除はデータベースを通じて行う。ここでのそれらの呼び出しは拒否される(409 managed_by_database)。そのスナップショットとフォークはプロジェクトで取得できる。データベースを構成するアプリも同じように示し(app.info().managed_by)、その更新、デプロイ、ロールバック、削除も同じく拒否される |
drives.snapshot(drive), drives.snapshot_with(drive, &DriveSnapshotOptions::new().name(…).fs_consistent()) |
スナップショットを取り、DriveSnapshot を返す。ノードが作る、オブジェクトストア内のドライブのバージョンで、何もコピーされない。実行中のサンドボックスが持っているドライブは、書き込みを短時間止めてスナップショットを取る。マシンを一時停止する(consistency は paused。停電時と同じ状態のドライブ)か、fs_consistent() ならサンドボックスのエージェントがファイルシステムを凍結する(frozen。ジャーナルを再生せずにマウントできる。エージェントが凍結できなければ 409 not_frozen)。最大 10 秒で、超えると 503 unavailable(再試行可能)となり、ゲストはそのまま動き続ける。実行中のサンドボックスが持っていないドライブは、そのまま取る(at_rest)。名前は英小文字、数字、ハイフンで 1〜63 文字(400 invalid_name) |
drives.snapshots(drive), drives.all_snapshots(drive_deleted), drives.get_snapshot(drive, snapshot), drives.delete_snapshot(drive, snapshot) |
ドライブのスナップショットの一覧(新しい順)。プロジェクトの全ドライブのスナップショット(Some(true) ならその後削除されたドライブのものだけ)。ID で指定した 1 つ(404 snapshot_not_found)。1 つの削除(その時点まで課金される)。そこから作ったフォークは必要なものを保持し、それを読み取り専用でマウントしているサンドボックスは、マウントが外されるまでマウントを保つ |
drives.fork(drive), drives.fork_with(drive, &DriveForkOptions::new().name(…).from_snapshot(…)) |
スナップショット、またはドライブの現在の状態を最初のバージョンとする新しいドライブ。何もコピーされず、すぐにマウントでき、サイズは元のドライブと同じで、parent_drive_id と root_drive_id を持つ。作成と同じく、プランのドライブ数の上限の対象(403 drive_limit_reached)。ほかのドライブやボリュームが使っている名前は 409 name_taken |
drives.restore(drive, snapshot) |
ドライブの最新のバージョンの後に、スナップショットの内容を持つ新しいバージョンを作る。その間のバージョンは、スナップショットかフォークが保持している限り残るため、古いスナップショットに復元した後に新しいスナップショットに復元することもできる。サンドボックスがドライブを読み書き可能で持っている間は拒否される(409 drive_attached。先にデタッチする)。ドライブを返す |
DriveOptions::new().history_seconds(n), drives.set_history(drive, Some(n)), drives.versions(drive), drives.version_at(drive, t), DriveForkOptions::new().at(t), drives.restore_to(drive, t); drive.set_history, drive.versions(), drive.restore_to(t) |
ドライブのバージョンのウィンドウ(slice DB8):時刻を指定して、どこまで遡って復元またはフォークできるか。3600〜86400 秒(指定しなければなし。オフにすると、保持していたものは消える)。書き込みがあった 30 秒ごとに、ある時点のドライブであるバージョンが 1 つウィンドウに加わり、それらが単独で保持するものは、スナップショットと同じく課金される。時刻を指定した復元やフォークでは、その時刻以前の最新のバージョンが使われる。その時刻を指定してバージョンの一覧を呼び出すと、そのバージョンが示される。versions は DriveWindowVersion を新しい順に返す。set_history(drive, None) でウィンドウをオフにする。DriveInfo::history_seconds、history |
DriveOptions::new().cache_bytes(n), drives.set_cache(drive, Some(n)); drive.set_cache(Some(n)), drive.info().cache_bytes, drive.info().cached_bytes |
ドライブのキャッシュ:ノードが NVMe に保持するドライブの最大量。指定しなければ全量(ドライブのサイズ)。0 まで減らせ、それを超えた分はオブジェクトストアだけにあり、ゲストが読むとそこから読み取られる(最も長く読まれていないものから手放し、ストアにまだないものは手放さない)。キャッシュは、ドライブが保持するデータとは別に、埋まっている量に応じて 1 時間ごとに課金される。小さくすると、超えた分はストアが保持した時点で手放される(実行中のサンドボックスが全量キャッシュでマウントしたドライブでは、そのサンドボックスが次にスリープまたは停止した時点から)。大きくすると、それ以降にゲストが読んだものを保持する。API ではドライブをボリューム(/v1/volumes)と呼び、クライアントは /v1/drives でアクセスする(以前と同じように応答する)。set_cache(drive, None) で再び全量にする。コントロールプレーンがキャッシュについて何も示さない場合、DriveInfo::cache_bytes はドライブのサイズ。cached_bytes は、現在ノードの NVMe にある量(どのノードも保持していなければ None)。使用量のレポートのグループと合計には cache_gb_hours がある |
drives.attach_snapshot(sandbox, &DriveMount::new(path, drive).snapshot(id)), drives.detach_snapshot(sandbox, snapshot), drives.force_detach_snapshot(sandbox, snapshot) |
既存のサンドボックスに、スナップショットを読み取り専用ですぐにマウントする(スナップショットなしのマウントは、何かを送る前に拒否される)。ドライブとスナップショットがその後削除されていてもいなくても、スナップショットの ID で外す(プロセスが使っている間は force_…)。どれも DriveSnapshot を返す。サンドボックスの作成時には CreateOptions::new().drive(DriveMount::new("/data", drive.id()).snapshot(snap.id())) |
drive.snapshot(), drive.snapshot_with(…), drive.snapshots(), drive.fork(), drive.fork_with(…), drive.restore(snapshot); snap.refresh(), snap.delete(), snap.fork(Some(name)), snap.restore(), snap.attach(sandbox, path), snap.detach(sandbox), snap.force_detach(sandbox) |
同じことを Drive と DriveSnapshot に対して行う。後者の info() は DriveSnapshotInfo:id(dsnap-…)、drive_id(その後削除されている場合もある)、name、consistency、used_bytes(取得時にドライブが保持していた量)、exclusive_bytes(単独で保持する量で、課金の基準。ストアのコレクターが計算するまでは 0)と exclusive_at、version(DriveVersion:number、epoch)、created_at |
- ドライブは一度に 1 つのサンドボックスのものです。ほかのサンドボックスが持っているドライブは拒否され(409
drive_attached)、アタッチ中のドライブの削除も拒否されます。キー別インスタンスのドライブは、キーとともに削除されます(409key_drive)。 - 別のノードが保持しているドライブは、先にサンドボックスのノードへ運ばれます。オブジェクトストアがまだ現在の状態を保持していない間(503
copy_behind)や、運べなかった場合(503drive_not_brought)、呼び出しはretryableです。クライアントはretriesの指定どおりに再試行し、それでも失敗すればエラーを返すので、少し後に再度要求してください。 - リージョンをまたぐ場合(slice D9)。ドライブのホーム(
home)とは、そのコピーを保持するオブジェクトストアのリージョンで、作成されたリージョンです。別のリージョンのサンドボックスも、そのリージョンのマシンがホームのストアを読める場合はマウントできます(読めなければ 409not_on_one_machine)。ドライブはバージョンとして手放され、そこで引き継がれ、ゲストが読むのに合わせてホームのストアから読み取られ、ゲストが書き込むものは今いるリージョンのストアに送られます(region)。ホームは、そこに 1 時間いると追従します。drives.move_home(drive)(drive.move_home())を使えばすぐに移ります。マシンがホームのストアに残っているものを自分のストアにコピーし、何も残らなくなった時点でhomeが新しいリージョンになります。DriveInfo::home_moveが移動の進み具合を示します(asked_at、asked_by、copied_objects、copied_bytes、objects_left、error)。古いホームのコピーは、保持しているどのバージョンも必要としなくなった時点で消えます(1 日後)。ドライブのホームが移るのは最大で 6 時間に 1 回で(409home_moved_recently)、ホームが現在のリージョンにあるドライブでは 409home_hereになります。 - プランの制限:無料プランでは最大 10 GiB のドライブを 3 つ(合計 15 GiB)、有料プランでは最大 1 TiB のドライブを 500 個です(403
drive_limit_reached)。 - スナップショットはドライブより長く残ります。ドライブの削除後も、ドライブの ID で一覧表示、読み取り、フォーク、読み取り専用でのマウントができます。単独で保持する量(
exclusive_bytes)に対して、1 時間ごとに、ドライブのデータの料金(1 GB-月あたり $0.02)で課金されます。 - フォークは、ほかのドライブと同じく、
used_bytes、つまり論理的な内容全体で課金されます。10 GB のスナップショットのフォークは、10 GB のドライブとして課金されます。 - スナップショットの読み取り専用のマウント:ドライブのリージョンのどのマシン上でも、ドライブを誰が持っていても、ドライブが削除された後でも、いくつものサンドボックスが同時にマウントできます。ブロックはゲストが読むのに合わせてオブジェクトストアから届きます。このキャッシュ外の読み取りに料金はかからず、使用量ではそのサンドボックスの下に表示されます(
UsageOptions::sandbox(id)、drive_cold_read_bytes)。そのサンドボックスのマシンがすでに保持しているものの読み取りにも料金はかかりません。ゲストは書き込めず、それをマウントしたサンドボックスは別のマシンへ移動できません。アプリのバージョンはドライブを読み書き可能でしかマウントせず、スナップショットの読み取り専用のマウントはサンドボックス用です。 - ノードがオブジェクトストアに何もコピーしない場合、ドライブにスナップショット、フォーク、復元はありません(409
not_copied)。 UsageOptions::drive(id)、kind("drive")、group_by(&["drive"])は、ドライブが保持した量、書き込まれた量、キャッシュ外から読まれた量を報告します。イベントdrive.created、drive.deleted、drive.attached、drive.detachedは、EventDriveかEventDriveAttachedを運びます。
client.volumes() は API の /v1/volumes で、ドライブの下にあるディスクです。移行のために残されています。これにしかないのは、ディスクがボリュームである単独で動くノードと、複数のサンドボックスが同時にマウントするファイルシステムである共有ボリュームです。コントロールプレーンは、プロジェクトのキーに対して通常のボリュームを作りません。
| 呼び出し | 内容 |
|---|---|
client.volumes().create_with(&VolumeOptions::new().name("team").shared()), volumes.get(id_or_name), volumes.list(), vol.delete() |
ボリュームの作成、取得、一覧表示、削除。サンドボックスは CreateOptions::new().volume(VolumeMount::new("/team", vol.id())) でマウントする。サンドボックスが持っているボリュームは削除できない(409 volume_in_use) |
**ボリュームのバックアップ。**バックアップは、ある瞬間のボリューム(ドライブもボリュームです)のコピーで、オブジェクトストアに、期限が来るまで保持されます。呼び出しによって、ボリュームのルールに従って毎日、またはサービスの新しいバージョンがインスタンスを置き換える前に取得されます。ドライブのスナップショットと違い、それ自体が独立したコピーで、それだけからボリュームを作ることができ、元のボリュームより長く残ります。ボリュームのバックアップは前回以降に変わったものだけを保持し、複数のバックアップが必要とするブロックは、そのうち最も古いもので 1 回だけ課金されます。ストアがすべてを受け取るまでは making、その後は ready で、それ以降はストアが保持する量(stored_bytes)に応じて、スリープ中のサンドボックスが保持するものと同じ料金で課金されます(バックアップ)。バックアップはコントロールプレーンの機能です。
| 呼び出し | 内容 |
|---|---|
client.volumes().backup(id_or_name), backup_with(id_or_name, &BackupOptions::new().expires_in_days(7)), vol.backup(), client.volumes().backups(id_or_name) |
今すぐバックアップを取り、Backup を返す。指定した日数(1〜365)、またはボリュームのルールの日数(設定しなければ 30 日)保持される。ボリュームのバックアップの一覧(新しい順。削除済みのボリュームのものは、その ID で)。オブジェクトストアに何もコピーしないノードにはバックアップはない(409 not_copied) |
client.backups().list(), list_of(volume_id), get(id), delete(id), restore(id, Some("data-back")) |
プロジェクトのバックアップの一覧(新しい順。削除済みのボリュームのものも含む)。1 つのボリュームのもの。1 つの詳細。1 つの削除(それ以降は課金されず、そこから作ったボリュームは残る)。準備ができたバックアップから作る新しいボリューム。client.volumes().create_with(&VolumeOptions::new().from_backup(id)) と同じ(作成中は 409 backup_not_ready、失敗後は 409 backup_unusable) |
b.refresh(), b.restore(name), b.delete() |
同じことを Backup に対して行う。その info() は BackupInfo:id(bak-…)、volume_id、volume_name(その時点のボリュームの名前)、status(making、ready、failed)、reason(manual、daily、before_version)、consistency(frozen、crash)、frozen_ms(ボリュームの書き込み元がコピーのために待った時間。凍結しなかった場合は None)、size_bytes(ボリュームが保持していた量)、volume_size_bytes(保持できた最大量。そこから作るボリュームが保持する量)、stored_bytes(課金の基準)、own_bytes(削除すると減る量)、shared_bytes、shared、region、failure、created_at、ready_at(課金の開始時刻)、expires_at |
- バックアップから作るボリュームは、バックアップ元のボリュームと同じサイズと種類で、バックアップのリージョンに作られます。
statusは、バックアップ全体を保持するまではrestoring、その後はreadyで、それまではアタッチできません。from_backupがバックアップを示します。配置先のマシンで復元できない場合、コントロールプレーンはリージョンの別のマシンにもう一度作ります。そのときrestore_attemptはSome(2)になり、first_failureが最初の試行に失敗した理由を示します。それも失敗するとfailed(failure)になり、削除しかできません。 UsageOptions::new().backup(id)、kind("backup")、group_by(&["backup"])は、バックアップが保持した量を、状態の計測項目(state_gb_hours)で報告します。client.billing()のmeters.state.backups_gb_hoursは、その計測項目のうちバックアップの分を示します。
ドメイン
プロジェクトのドメインは、デフォルトのアドレスと同じくアプリへ、またはサンドボックスのポートへ向かう名前です。ドメインはコントロールプレーンの機能です。
| 呼び出し | 内容 |
|---|---|
client.domains().create(domain, &DomainOptions::new().app(…)) または .sandbox(…).port(…)(.proxied(…) を指定可能)。get、list()、list_of("app_id", app)、delete。domain.update(&DomainOptions…)、domain.refresh()、domain.delete() |
アプリまたはサンドボックスのポートのための名前。info().verification のレコードが DNS に現れるまでは pending_verification(独自ドメイン)。プロジェクトにすでにあれば 409 domain_exists |
ブラウザ
**未公開。**Glac のブラウザはまだ利用できません。以下は公開に先立つ、現時点での SDK です。
セッションは Glac がホストする Chromium で、Chrome DevTools Protocol(CDP)のクライアントが、その cdp_url(Playwright の connectOverCDP や Puppeteer の browserURL に渡すもの)から外部で操作します。クライアントの接続中は稼働し、最後のクライアントが離れると数秒後に、ページを保持したままスリープします。アドレスには認証情報が含まれるため、{:?} はアドレスを含みません。
| 呼び出し | 内容 |
|---|---|
client.browsers().create(), create_with(&BrowserOptions::new().name("agent-42")) |
稼働中の新しいセッション。BrowserOptions には、kind(デフォルトの chromium-headless、画面付きの chromium、またはレイアウトエンジンのないブラウザである lightpanda)、name、region、sleep_after_seconds、connection_idle_seconds、delete_after_idle_seconds、metadata、profile(name, save) と profile_version(name, version, save)(セッションを返す前に読み込むプロジェクトのプロファイル。save なら削除時に保存し直す)、proxy(url)(CONNECT を受け付ける自前の HTTP プロキシ。有料プランのみ。セッションのポート 80 と 443 への接続は、ブラウザの隣にあるリレーによってそこを経由して送信され、認証情報はプラットフォームが保持する。無料プランでは 403 proxy_not_on_plan)がある。既存のセッションの名前を指定すると、そのセッションがページとともに復帰して返される。実行前にサンドボックスが失敗したものはセッションにならず、作成は 503 session_failed で拒否される |
client.browsers().get(id_or_name), delete(id_or_name), delete_without_saving_profile(id_or_name) |
1 つを取得。終了(保存するプロファイルを指定して作ったセッションは、保存なしで終了しない限り、先にプロファイルを保存)。BrowserSession は、id、kind、status、cdp_url、live_url、profile(name、save、version)、proxy(自前のプロキシの url と username。パスワードは含まない)、各種の時刻、awake_seconds、そしてプロキシを双方向に通過した量 proxy_bytes(proxy_sent_bytes、proxy_received_bytes)を持つ |
session.status, session.error |
awake、asleep、unavailable(ノードに到達できない)、failed(作成に応答した後でサンドボックスが失敗した。たとえばブラウザが停止して戻らなかった)。error は理由を BrowserError(ノードが返したとおりの code、message)で示し、失敗していなければ None。イベント browser.failed もそれを伝え、EventBrowserFailed::of(&event) でそのデータを読む |
セッションは、ほかのインスタンスと同じく、使用したキューブのリソースで課金されます。処理している間の CPU、稼働中のメモリ、送信トラフィックです(料金とプラン)。session.awake_seconds は稼働していた時間です。
一覧表示、変更、スリープ、新しいアドレスの発行は、Python と TypeScript の SDK と glac browsers にあります。セッションはコントロールプレーンの機能です。
データベース
データベースはマネージド Postgres です。ドライブ上で動く専用の PostgreSQL で、何も要求がないときはスリープし、接続があればセッションを保ったまま復帰します。オーナーのパスワードは create と reset_password のレスポンスにしか含まれず、どこにも保存されません。そのどちらかが返した Database の connection_string() には、パスワードが含まれます。
| 呼び出し | 内容 |
|---|---|
client.databases().create("shop"), create_with("shop", &DatabaseOptions::new().version("18")) |
データベースを作成し、最初の起動を待つ(status は ready、または creating。その場合は準備ができるまで get する)。DatabaseOptions には、version、region、database_name、size(vcpus, memory_bytes)、storage(size_bytes, durability)、sleep_after_seconds、suspend_after_seconds(メモリを保存して本当にスリープするまで一時停止したままでいる時間。その間の接続には 1〜2 ミリ秒で応答する。1〜86400 秒、デフォルトは 300 秒)、wake_daily_at("03:30")(休止中に毎日その時刻に復帰させる)、metadata がある。既存のデータベースの名前を指定すると、パスワードなしでそのデータベースが返される。databases.set_suspend_after(db, Some(n)) と set_wake_daily_at(db, Some("04:00")) は、この 2 つを全ブランチまとめて変更し、None でそれぞれ元に戻す(300 と、なし)。無料プランでは、Neon の無料プランがゼロまでスケールするのと同じく、データベースの休止の設定は固定:最後のクエリの 1 秒後に一時停止し(sleep_after_seconds は 1)、その 5 分後にスリープする(suspend_after_seconds は 300)。ほかの値は、作成でも変更でも 400 invalid_field で拒否される |
client.databases().get(id_or_name), list(), list_named(name), delete(id_or_name, final_snapshot) |
1 つを取得。一覧。ブランチごと削除(final_snapshot が false でなければ、ドライブのスナップショットを 7 日間保持) |
client.databases().branches(db), branch(db, "main"), sleep(db, "main"), wake(db, "main"), reset_password(db, None) |
ブランチ(最初に main、次に独自のブランチ)、そのうちの 1 つ、ブランチをすぐにスリープまたは復帰させる操作、そしてオーナーの新しいパスワード(このレスポンスにだけ含まれる) |
client.databases().create_branch(db, "preview", &DatabaseBranchOptions::new().parent("main").expires_at(t)), delete_branch(db, branch) |
独自のブランチ:親(指定しなければ main)の現在の状態を、何もコピーせずに引き継ぎ、プロジェクトのネットワーク上の <database>-<name> に専用の PostgreSQL、エンドポイント、ドライブを持つ。有効期限を指定すると、その時刻に自動で削除される。ロールとパスワードは親のもの(branch.connection_string(Some(owner_password)))。削除は PostgreSQL を止めてドライブを削除する。main(409 root_branch)や、ほかのブランチの作成元になったブランチ(409 has_branches)は削除できない |
DatabaseOptions::new().history_seconds(n), databases.set_history(db, n), databases.restore(db, t), databases.restore_exactly(db, t), DatabaseBranchOptions::new().at(t), at_exactly(t) |
履歴(slice DB8 と DB5):時刻を指定して、ルートブランチをどこまで遡って復元できるか、またはブランチを作れるか。指定しなければ 1 日で、有料プランでは 0(なし)〜30 日、無料プランでは 1 日。復元はルートブランチをその場で戻す。分ちょうどの時刻やバージョン自体の時点なら、その時刻以前で最新の、ドライブのウィンドウのバージョンへ(書き込みがあった 30 秒ごとに 1 つ、最大 1 日分)。それより前や秒単位の時刻、または exactly を指定した場合は、WAL アーカイブによってその時点そのものへミリ秒単位で戻す(WAL をその時点まで再生する。数秒〜数分かかり、呼び出しは待つ)。エンドポイント、接続文字列、オーナーのパスワードは変わらず、接続は切断され、復元前の状態は独自のブランチ main_old_<the restore's moment> として保持される。新しいバージョンが 5 分以内に応答しなければ取り消される(503 database_restore_failed)。時刻を指定したブランチは、代わりにその時点から作られる(from: {at, exact})。履歴にない時刻は 400 invalid_history になり、そのメッセージは、ウィンドウとアーカイブがいつから遡れるかを示す。秒単位の時刻(または exact)はアーカイブから戻すか拒否され、その前のウィンドウのバージョンが使われることはない。アーカイブが届かない場合(アーカイブを保持しないデータベース、または最も古いベースより前の時刻)は 400 invalid_history、アーカイブのストアがセグメントを拒否している間は 503 database_archive_unavailable(再試行可能)。どの拒否も、その時刻以前のウィンドウのバージョンを示すので、代わりにそのバージョン自体の時点を指定できる。ストアがブロックを返さない場合は 503 drive_unreadable(再試行可能)で拒否され、何も作られない。オブジェクトストアの障害が長引き、ドライブがログのために保持できる量を超える WAL が待つことになると、データベースを動かし続けるためにその WAL は手放される。これがアーカイブの欠落で、history.gaps(DatabaseHistoryGap:from、to、from_lsn、to_lsn)に示される。手放した WAL の開始時刻、アーカイブがどの時刻も復元できない期間の終わり(その後に取った最初のベースの終わり。それまでは null)、ログ内での開始位置と終了位置。欠落の間の時刻は、ドライブのウィンドウからしか復元できず(分ちょうどの時刻、またはバージョン自体の時点)、秒単位や exact では 400 invalid_history。欠落はイベントでも伝えられる。最初に見つかったとき(データベースの稼働中に、欠落ごとに 1 回)に database.history_gap、その後アーカイブが再び復元できるようになったときに database.history_resumed。EventDatabaseGap::of(&event) はそれらのデータを読む(ほかの種類のイベントなら None):データベース、欠落の from、to(前者では None)、from_lsn と to_lsn、segments(手放した 16 MB のセグメントの数)、そして前者では error(オブジェクトストアの最後の拒否の内容)。Database::history_seconds、history |
DatabaseOptions::new().tiering(true), databases.set_tiering(db, on) |
階層型ストレージのオンとオフ(slice DB18:有料プランで、オブジェクトゲートウェイが提供されている場合のみ。階層化されたパーティションを持つブランチがある間、オフは 409 tiered_data_exists で拒否される。そのために各ブランチの PostgreSQL が 1 回再起動する)。Database::tiering は、オンかどうかと、現在の課金の基準(tiered_bytes)を示す |
client.databases().roles(db), reset_role_password(db, role) |
ルートブランチのログインするロール。PostgreSQL 自体から読み取る(DatabaseRole:name、owner):オーナーと、作成したロール(CREATE ROLE … LOGIN)で、プラットフォームのロールは含まない。そして、そのどれかの新しいパスワード。このレスポンスにだけ含まれる:ロールの password と、そのロールでのルートブランチの接続文字列(connection_string())を持つ DatabaseRoleSecret。オーナーの接続文字列は、API が返すデータベースから作られる。独自のブランチは、作成時のパスワードを保持する |
db.connection_string(), branch.connection_string(Some(password)) |
tokio-postgres、sqlx、psql に渡せるもの:API が返す場合はパブリックなもの、そうでなければプロジェクトのネットワーク上のもので、オーナーのパスワード(パーセントエンコード)入り。db.default_branch は connection_uri、pooled_uri、private_uri、endpoint、state を持つ |
データベースには、プロジェクトのネットワーク上で、そのサンドボックス(project_network で作成)とサービスから名前でアクセスします。これが private_uri です。エッジが PostgreSQL を提供する場合は、パブリックアドレスでどこからでもアクセスできます(connection_uri、ポート 5432、TLS のみ。プーラーは pooled_uri)。提供するエッジがない場合や、データベースに名前を付けるリージョンがない場合は None で、接続文字列は、それらがあればそれらを使います。データベースやブランチの {:?} は、パスワードも、パスワード入りの接続文字列も表示しません。データベースはコントロールプレーンの機能です。
マネージドアプリケーション
カタログのアプリケーション(たとえば Rivet)は、プロジェクトに専用のキューブのリソースとしてインストールされます。アプリ、ボリューム、シークレット、バケットで、インストールのラベルが付き、変更はインストールを通じてのみ行い、それぞれのリソースとして課金されます(マネージドアプリケーション)。インストールのレスポンスには、秘密のもの(管理者トークン、それを含むエンドポイント)も含め、すべての出力値があり、読み取りでそれらが表示されるのは admin スコープを持つキーだけです。インストールのオブジェクトは、以前のレスポンスが返し、後のレスポンスが隠した出力値をそれぞれ保持します。
let rivet = client.catalogue().get("rivet", None)?; // its parameters, and a month of it in Cube's meters
println!("{:?}", rivet.month);
let engine = client.installations().create("rivet", &InstallationOptions::new().parameters(json!({"disk_gib": 20})))?;
println!("{}", engine.info().outputs["endpoint"]); // RIVET_ENDPOINT, its token in it| 呼び出し | 内容 |
|---|---|
client.catalogue().list(), client.catalogue().get(app, version) |
カタログのアプリケーションの一覧。それぞれ CatalogueApp(app、version、title、summary、verified、licence、versions、parameters:JSON Schema、outputs、month、そしてエントリ全体の raw)。1 つのアプリケーション(最新バージョン、または指定したもの)と、raw に入ったその定義 |
client.installations().create(app, &InstallationOptions::new().name(..).version(..).region(..).parameters(..).requires(name, installation).no_wait()) |
インストールし、準備ができるまで待つ(no_wait ならすぐに戻る)。失敗すると、種類とコードが installation_failed で、その説明を持つエラーになる |
client.installations().list(app), client.installations().get(id_or_name) |
プロジェクトのインストールの一覧(新しい順。None ならすべて)。1 つのインストール |
installation.update(&InstallationUpdate::new().version(..).parameters(..)), delete(), refresh(), wait(), wait_for(limit) |
別のバージョンへの変更、またはパラメーターのマージパッチによる変更(null でデフォルトに戻す)。構成するものごとの削除(status():deleted または deleting)。wait_for は wait_timeout で終わり、インストールはそのまま続く |
installation.id(), name(), status(), info() |
API が返すとおり:info() は outputs、secret_outputs、parameters、apps、volumes、buckets、requires、failure を持つ |
マネージドアプリケーションはコントロールプレーンの機能です。
シークレットと変数
| 呼び出し | 内容 |
|---|---|
client.secrets(): put_with("DB_URL", &url, Some(&["db.example.com"])), put, update(name, hosts), list, delete |
API が名前の下に保持し、二度と返さない値と、それぞれを送ってよいホスト。アプリの環境変数、イメージのプルシークレット、サンドボックスのエグレスルールで使う |
client.variables(): put(name, value), list, delete |
プロジェクトの共有変数。サービスの env から参照する("${{ shared.REGION }}")。コントロールプレーンの機能 |
プロジェクト、アカウント、請求
| 呼び出し | 内容 |
|---|---|
client.account(), client.update_account(&AccountOptions) |
アカウント:その月のインターネットへのトラフィックと、そのトラフィックが止まる上限。通知の送信先(email。AccountOptions::new().email(…) で設定。email_verified、そしてそのアドレスへのメールが不達の間は email_undeliverable(EmailUndeliverable):通知の送信先を参照)。通知の言語(language。AccountLanguage:code は “en”、“de”、“fr”、そしてその source。AccountOptions::new().language("de")、または再び請求先住所に従うなら no_language())。請求先住所と税番号。コントロールプレーンの背後では keeps(Option<AccountKeeps>):キーのプロジェクトが保持するドライブとボリュームを、作成時に適用される数値で、それぞれ持っている量とともに示す(volumes、volume_bytes、volumes_bytes(上限なしなら limit は None)、unsized_bytes:上限の引き上げを参照) |
client.usage_report(&UsageOptions), client.usage(&ListOptions) |
1 時間ごとの帳簿による、グループ別の使用量。サンドボックスごとの使用量 |
client.billing(), client.update_billing(&BillingOptions), client.balance(), client.update_balance(&BalanceOptions), client.top_up(usd), client.top_up_with(usd, &TopUpOptions), client.charges(), client.ledger() |
プランと請求、残高、自動チャージとチャージ、カードへの請求と台帳。コントロールプレーンの機能 |
client.project(), client.events_page(&EventsOptions) |
キーのプロジェクトと、そのイベントの 1 ページ分。コントロールプレーンの機能 |
**イベントのデータ。**コントロールプレーンのイベントの種類ごとに、そのデータの型がこのクレートにあり、説明のスキーマにちなんだ名前が付いています。その of(&event) は、対応する種類のイベントを読み、ほかの種類なら None を返します(glac::EventDatabase::of(&e))。データベースのもの:EventDatabase(database.created:region と PostgreSQL の version 付き。database.deleted:7 日間保持されるドライブのスナップショット final_snapshot_id 付き)、EventDatabaseBranch(database.branch_created:parent 付き。database.branch_deleted:reason 付き)、EventDatabaseRestored(database.restored:時点 at と、復元前の状態を保持するブランチ old_branch_id)、EventDatabaseArchive(database.archive_failing:since、連続して失敗したセグメントの数 failures、その間に復元で遡れる範囲 archived_until、オブジェクトストアの error。database.archive_recovered:recovered_at)、EventDatabaseGap。ほかも同様です。サンドボックスのもの(EventSandboxSlept、EventSandboxWoke、EventSandboxMoved、EventSandboxDeleted など)、アプリとそのキーのもの(EventApp、EventAppFailed、EventKeyCreated など)、プロジェクトのデプロイとキー、バックアップ、ドメイン、エクスポート先、ドライブのスナップショットのもの(EventProjectDeployed、EventBackup、EventDomain、EventDestination、EventDriveSnapshot など)、そしてアカウントのプラン、チャージ、返金、撤回、残高、クレジット、ストレージ、送信量の上限、利用上限額、保留、未払いのもの(EventSubscriptionPeriod、EventTopupCollected、EventWithdrawalReceived、EventBalance、EventInDebt など)です。その種類のすべてのイベントにあるわけではないフィールドや、null のフィールドは Option です。
Webhook、API キー、利用上限額、プロジェクト。client.webhooks().create_with(url, &WebhookOptions::new().events(&[…]).enabled(true)) は、プロジェクトのイベントを自前のアドレスに送ります(各配信に署名する secret は、このレスポンスにだけ含まれます)。get、list、update、delete もあります。client.keys().create_with(&KeyOptions::new().name(…).scopes(&[…])) は API キーを作ります(キーそのもの key は、このレスポンスにだけ含まれます)。get、list、delete もあります。client.limits() と client.update_limits(Some(50.0)) は、プロジェクトの利用上限額と今月の支出額で(None で上限を外します)、sbx.limits() と sbx.update_limits(…) はサンドボックス自体のものです。client.request_limit(limit, wanted, why) は、アカウントの申請による上限の引き上げを 1 つ申請します(名前で指定する上限、"volumes"、"volume_bytes"。バイト数の 2 つはバイト単位。同じ上限の申請が待機中なら 409 limit_request_pending)。client.limit_requests(limit, cursor) と client.limit_request(id) は、申請をオペレーターの回答とともに読み取ります(LimitRequest:status は pending、granted、refused。granted、answer)。EventInstanceRestarted::of(&event)、EventInstanceUpgrade::of(instance.upgraded、.reverted、.upgrade_failed)、EventLimitRequest::of は、それらのイベントのデータを読みます。instance.start_command()(サンドボックスでは info().start_command)は、起動コマンドの実行状況、マシン内で再起動された回数、最後の終わり方を示します。client.update_project(Some(30)) は、スリープが何日続いたらサンドボックスを削除するかを設定します(None なら削除しない)。コントロールプレーンの機能です。Webhook と API キー、プロジェクトの上限の設定、update_project には admin スコープを持つキーが必要で、サンドボックスの上限の設定には write が必要です。
ログのエクスポート先、監査ログ、請求関連の書類。client.destinations() は、プロジェクトのアプリのログ(とメトリクス、リクエストのトレース:.signals(&["logs", "traces"]))のエクスポート先(OpenTelemetry のエンドポイント)です:create(name, &DestinationOptions::new().endpoint(u).header(name, value))(作成時にすぐ 1 件のレコードで試す:status.connected)、get、list、update、delete、test(すぐに 1 件送る)。ヘッダーの値は認証情報で、エクスポート先の作成時か変更時に送られ、どのレスポンスにも含まれません(返されるのはヘッダーの名前だけです)。client.audit_page(&ListOptions::new().limit(n)) は、プロジェクトの監査ログの 1 ページ分を新しい順に返します(実行者、メソッド、パス、ステータス)。読み取り以外のすべての呼び出し(拒否されたものも含む)と、オペレーターによるアプリのログの読み取りすべてです。client.statement(Some("2026-09")) は 1 か月の明細(None なら今月)で、データベースごとの行(databases:その月の使用量、履歴と階層化データを別記したもの、それを定価で換算した額)を含みます。client.invoices() は Stripe にある請求書(PDF のリンク付き)、client.notices() はアカウントに送られたメール、client.withdrawals(&ListOptions::new()) は今撤回できる購入と行った撤回(EEA または英国の消費者は、購入から 14 日以内であれば撤回できます)、client.withdraw(charge_id, name, Some(email)) は購入者自身による撤回の意思表示で、すぐに受理されます。コントロールプレーンの機能です。エクスポート先の作成、変更、テスト、削除、監査ログの読み取り、撤回には admin スコープを持つキーが、それ以外には読み取りのできるキーが必要です。
**すぐに受け取る購入(start_now)。**欧州経済領域または英国の消費者(確認済み、または確認中の税番号を持たないアカウント)は、購入から 14 日以内であれば撤回でき、購入したものがすぐに提供されるのは、本人が明示的に求めた場合だけです。start_now() がその要求を伝えます。client.top_up_with(20.0, &TopUpOptions::new().start_now())、BillingOptions::new().plan("paid").start_now()(無料プランから購入する有料プラン)、BalanceOptions::new().auto_reload(true).start_now()(自動チャージがオンの間のすべての自動チャージ)で、コンソールのチェックボックスの横に表示される文言(GET /v1/billing/withdrawals の right.start_now にもあるもの)で伝えます。
撤回できる 14 日間のうちに、この購入をすぐに提供するよう求めます。撤回した場合は、それまでに提供された分を支払います。チャージのクレジットをすべて使った時点で全額が提供されたことになり、それ以降は撤回できなくなることを理解しています。
サービスはすぐに始まります。14 日以内なら購入者はまだ撤回でき、その場合はそれまでに提供された分を支払います。すべてが提供されると(チャージのクレジットをすべて使うと)、撤回する権利はなくなります。この要求は、購入する本人が求めたときだけ指定してください。SDK は、指定されない限り決して送りません。指定がなければ、そのようなアカウントは拒否されます。ステータス 400、コード start_now_required、param() が start_now の Error で、国名を挙げ、要求の文言を引用したメッセージが付き、何も購入されません。要求なしで自動チャージをオンにしたアカウントでは何もチャージされず、月に 1 回通知されます(イベント auto_reload.no_request。EventAutoReloadNoRequest::of で読みます。カードが登録されていないアカウントでは auto_reload.no_card と EventAutoReloadNoCard)。
エラー
0 以外の終了コードは結果です。res.exit_code を読んでください。API のエラーは、status()、kind()、code()、param()、retryable()、request_id()、message()、そしてエラーのページの該当するコードを指す docs_url() を持つ glac::Error です。接続できない場合やタイムアウトも同じで、status() は None です。タイムアウトかどうかは is_timeout() でわかります。お金に関するエラー(402 account_in_debt、403 trust_limit_exceeded、409 no_card、400 start_now_required など)は、コントロールプレーン自身のメッセージを持ち、解除する方法を示します。
知っておくべき動作
- **スリープ中のサンドボックス。**サンドボックスがスリープ中や起動中であることを理由に失敗する呼び出しはありません。待ってから続行します。
res.wakeは、runが復帰をどれだけ待ったかを示します。クライアントのon_responseは、すべての呼び出しについてそれを受け取ります。 - **再試行。**応答がない場合や
retryableのエラーは、0.2 秒から倍々に間隔を空けて 2 回再試行されます(retries)。すべてのPOSTにはIdempotency-Keyが付きます。idempotency_key(key)を使えば、再起動したプログラムが、以前に作ったものを取り戻せます。 - タイムアウト。
timeout(指定しなければ 15 分)は API を待つ時間で、RunOptions::timeoutはコマンド自体の制限時間です。 - スレッド。
Clientはスレッド間で共有でき、その製品のハンドルはクライアントの接続を使います。Sandboxに対する読み取りのみの呼び出しは&selfを取ります。tokio では、非同期版ができるまでtokio::task::spawn_blocking(move || sbx.run("make")).await??を使ってください。 - HTTPS には組み込みの Mozilla のルート証明書を使い、
SSL_CERT_FILEで独自の証明書を追加できます。これまでのところ、平文の HTTP の開発マシンに対してしか動かしていません。