Packages
gcp_compute
0.2.0
Spawn and manage Google Compute Engine instances over the REST API, with pluggable auth, telemetry, and an ergonomic instance builder.
Current section
Files
Jump to
Current section
Files
lib/gcp_compute/error.ex
defmodule GcpCompute.Error do
@moduledoc """
Normalized error returned by every `GcpCompute` function.
Errors are returned as `{:error, %GcpCompute.Error{}}`. The struct is also an
exception, so it can be raised by the bang variants (`insert_instance!/3`, …)
or with `raise/1`.
## Fields
* `:reason` — a coarse, matchable atom (`:api_error`, `:transport`,
`:timeout`, `:token_fetch_failed`, `:operation_failed`, …).
* `:status` — HTTP status code, when the error came from an API response.
* `:message` — human readable message (from GCP when available).
* `:errors` — the raw `error.errors` list returned by the Compute API.
* `:body` — the underlying body/exception, for debugging.
* `:operation` — the failed `GcpCompute.Operation`, for operation errors.
"""
@type t :: %__MODULE__{
reason: atom(),
status: non_neg_integer() | nil,
message: String.t() | nil,
errors: list() | nil,
body: term(),
operation: GcpCompute.Operation.t() | nil
}
defexception [:reason, :status, :message, :errors, :body, :operation]
@impl true
def message(%__MODULE__{message: message}) when is_binary(message), do: message
def message(%__MODULE__{reason: reason, status: status}) when is_integer(status) do
"GCP Compute error (#{reason}, HTTP #{status})"
end
def message(%__MODULE__{reason: reason}), do: "GCP Compute error: #{inspect(reason)}"
@doc """
Build an error from a non-2xx Compute API response.
Handles the standard Google error envelope:
%{"error" => %{"code" => 403, "message" => "...", "errors" => [...]}}
"""
@spec from_response(non_neg_integer(), term()) :: t()
def from_response(status, %{"error" => %{} = error} = body) do
%__MODULE__{
reason: :api_error,
status: status,
message: error["message"],
errors: error["errors"],
body: body
}
end
def from_response(status, body) do
%__MODULE__{reason: :api_error, status: status, message: "HTTP #{status}", body: body}
end
@doc "Build a transport-level error from a `Req`/`Mint` exception."
@spec from_exception(Exception.t()) :: t()
def from_exception(exception) do
%__MODULE__{
reason: :transport,
message: Exception.message(exception),
body: exception
}
end
end
defimpl Inspect, for: GcpCompute.Error do
import Inspect.Algebra
# `:body` may hold a Req/Mint exception whose request headers carry the bearer
# token. Redact it so errors are safe to log / inspect / send to Sentry.
def inspect(error, opts) do
fields = [
reason: error.reason,
status: error.status,
message: error.message,
errors: error.errors,
body: error.body && :REDACTED,
operation: error.operation
]
concat(["#GcpCompute.Error<", to_doc(fields, opts), ">"])
end
end