Every gcp_compute call in the project, and the error vocabulary built on
top of it.
The same confinement CrowdControl.Backend.Docker.API and
CrowdControl.Backend.Kubernetes.API provide: one file to audit when the
client library moves, and one place where %GcpCompute.Error{} becomes
{:error, {:gce, _}}. CrowdControl.Provider.Gce never handles a
GcpCompute struct — not even a successful one. get_instance/3 and
list_all/3 return plain maps, which is also what lets the provider's tests
build instances by hand.
Vocabulary
{:gce, {:not_found, message}}— HTTP 404, wherever it surfaced.Gce.release/1treats it as success, which is what makes teardown idempotent.{:gce, {:http_status, status, message}}— any other non-2xx.{:gce, {:transport, message}}— the request never got an answer.{:gce, {:operation_timeout, message}}— aninstances.insertorinstances.deleteoperation that did not reachDONEin time. The instance may exist anyway, which is why the provider rolls back on this.{:gce, {:operation_failed, message}}— reachedDONEcarrying an error.{:gce, {:token_fetch_failed, message}},{:gce, {:missing_token, _}}— credentials, decided before the network.{:gce, {:bad_spec, message}}—GcpCompute.Instance.spec/1rejected the options, before any VM existed.{:gce, {:bad_config, message}}— no usable client config.{:gce, {:list_page_limit, pages}}— pagination did not terminate. An error, never a short list; seelist_all/3.
Only messages travel, never %GcpCompute.Error{}'s :body: that field can
hold the Req exception whose request headers carry the bearer token. The
dep's own Inspect redacts it, but an error tuple that reaches a crash
report or Logger metadata is not always rendered through Inspect.
Summary
Functions
Build the client config for opts.
Delete an instance and wait for the operation, treating 404 as success.
Raise unless the optional :gcp_compute dependency is available.
Read one instance.
Create an instance and wait for the operation to finish.
Build and validate the instances.insert body.
Every instance matching filter, following nextPageToken to the last page.
The project a config points at.
The zone a config points at.
Types
@type config() :: struct()
A %GcpCompute.Config{}.
Opaque here on purpose: it belongs to the optional dep, it holds a live token-provider argument, and it must never reach a persisted handle.
@type instance() :: %{ name: String.t() | nil, status: String.t() | nil, external_ip: String.t() | nil, internal_ip: String.t() | nil, created_at: DateTime.t() | nil, labels: %{optional(String.t()) => String.t()}, metadata: %{optional(String.t()) => String.t()} }
One instance, flattened to plain data.
external_ip/internal_ip are the first of each; metadata is the
metadata.items list collapsed into a map, which is where the raw owner and
the agent token live.
Functions
Build the client config for opts.
Either a %GcpCompute.Config{} passed as :gce_config, or one built from
:project, :zone, :token_provider (and the rest of
GcpCompute.Config.new/1's options).
Application env under :gce fills in whatever opts omits. That fallback is
the reattach path rather than a convenience: a persisted handle carries no
token provider — it would be a live credential at rest — so the node that
reconnects gets its client config from configuration, not from the Store
record.
config :crowd_control,
gce: [project: "my-project", zone: "us-central1-a"]
Delete an instance and wait for the operation, treating 404 as success.
Already-gone is the desired end state, which is what makes
Gce.release/1 idempotent across the several teardown
paths that call it.
@spec ensure_gcp_compute!() :: :ok
Raise unless the optional :gcp_compute dependency is available.
Called from Gce.acquire/1 only, so that merely
loading this module — as CrowdControl.Reaper does when it walks configured
backends — never raises.
Read one instance.
Create an instance and wait for the operation to finish.
The operation, not the guest: this returns while Debian is still booting and
the guest agent has not yet seen the SSH key. Reachability is
CrowdControl.Provider.Gce.Tunnel's problem and readiness is
GET /v1/health's.
Build and validate the instances.insert body.
Fails before anything is created, which is the only failure on the acquire path that needs no rollback.
Every instance matching filter, following nextPageToken to the last page.
Never GcpCompute.Instances.list/2: it returns one page and no
indication that there were more. A short list is not a cosmetic bug here.
CrowdControl.Reaper reads this as the evidence of what is live, so a live
sandbox missing from it is live? = no, stored? = yes — and the reaper
deletes the store record of a running, billed VM, orphaning it permanently.
Truncation must therefore be impossible, and any page failure surfaces as
{:error, _} rather than a shorter list. This is the same hazard, and the
same shape, as CrowdControl.Backend.Kubernetes.API.list_all/3.
The project a config points at.
The zone a config points at.
Read back out of the built config rather than out of the caller's options, so
that :gce_config and [project:, zone:] produce identical handles and the
library's own default zone can never disagree with the handle's.