Documentation
¶
Overview ¶
Package cloud detects the host's cloud provider (AWS, GCP, Azure) via the provider-specific instance metadata service (IMDS) and exposes the result as a tag map prefixed cloud:* that alpacon-server matches against CloudInstance records during reconcile.
Index ¶
Constants ¶
const ( ProviderAWS = "aws" ProviderGCP = "gcp" ProviderAzure = "azure" )
Provider names.
const ( TagProvider = "cloud:provider" TagInstanceID = "cloud:instance_id" TagRegion = "cloud:region" TagAvailabilityZone = "cloud:availability_zone" TagInstanceType = "cloud:instance_type" TagNetworkID = "cloud:network_id" TagAccountID = "cloud:account_id" )
Tag keys reported to alpacon-server. These match alpacon-server's _build_cloud_tags schema (cloud_plan/tasks/PHASE3D).
Variables ¶
var ErrNoCloudProvider = errors.New("no cloud provider detected")
ErrNoCloudProvider is returned by Detect when no provider responds on the link-local IMDS endpoint. This is the normal on-prem / dev-laptop / non-cloud VM path — callers should treat it as a graceful degrade, not a failure.
Functions ¶
func Detect ¶
Detect probes the supplied providers in order and returns the first one that responds on its IMDS endpoint. On Linux, a DMI hint is consulted first so we don't burn 800 ms probing the wrong provider on the common case (single-cloud hosts have a strong identifying string in sys_vendor / chassis_asset_tag).
Return contract:
- all probes fail and ctx is healthy: returns (nil, nil, ErrNoCloudProvider). Callers must treat this as a normal on-prem / dev path, not a failure.
- ctx is canceled or times out at any point: returns (nil, nil, ctx.Err()). This is distinct from ErrNoCloudProvider so callers can tell "no provider" apart from "detection aborted by timeout/cancel" — including the edge case where ctx expires during the last provider's Probe.
- probe succeeds and Fetch succeeds: returns (provider, fullMeta, nil).
- probe succeeds but Fetch errors mid-read: returns (provider, partialMeta, fetchErr). Caller gets to decide how to surface the partial result. We never fall through to another provider once a probe is positive — IMDS responses are provider-specific, so partial data is strictly better than wrong-provider data.
Detect is a pure library function: it does not log. Callers that need to surface failures (e.g. the register CLI) should inspect the returned error and format it in their own output style.
Types ¶
type AWSProvider ¶
type AWSProvider struct {
// contains filtered or unexported fields
}
AWSProvider implements Provider against EC2 IMDSv2.
func NewAWS ¶
func NewAWS() *AWSProvider
NewAWS returns an AWS provider targeting the link-local IMDS endpoint.
func NewAWSWithBase ¶
func NewAWSWithBase(base string) *AWSProvider
NewAWSWithBase constructs an AWS provider against an arbitrary IMDS base URL. Tests inject an httptest server URL here.
func (*AWSProvider) Fetch ¶
func (p *AWSProvider) Fetch(ctx context.Context) (*Metadata, error)
Fetch reads the instance-identity document and the primary ENI's VPC ID. If Probe-positive but Fetch encounters an error mid-read, we return whatever partial Metadata we managed to populate and let the caller log — we do NOT fall back to a different provider, because the host IS on AWS.
On any failure path Fetch returns a non-nil *Metadata with at least Provider=aws set, matching the GCPProvider/AzureProvider contract so callers can call .ToTags() safely without nil-guarding the result.
type AzureProvider ¶
type AzureProvider struct {
// contains filtered or unexported fields
}
AzureProvider implements Provider against Azure Instance Metadata Service.
func NewAzure ¶
func NewAzure() *AzureProvider
NewAzure returns an Azure provider pointed at the link-local IMDS endpoint.
func NewAzureWithBase ¶
func NewAzureWithBase(base string) *AzureProvider
NewAzureWithBase builds an Azure provider with an explicit base URL. Used by tests.
func (*AzureProvider) Fetch ¶
func (p *AzureProvider) Fetch(ctx context.Context) (*Metadata, error)
Fetch reads /metadata/instance and maps the response into Metadata. Azure IMDS exposes everything in a single JSON document, so one HTTP call covers all fields. NetworkID (VNet) is intentionally left empty: Azure IMDS exposes only subnet prefix, not the VNet name — getting VNet requires ARM API + managed identity, which is out of scope for V1.
func (*AzureProvider) Probe ¶
func (p *AzureProvider) Probe(ctx context.Context) bool
Probe issues the standard /metadata/instance request with the required Metadata: true header and api-version query parameter. The combination of path, header, and query param is the discriminator: AWS responds 401 on /metadata/instance without a session token, and GCP responds non-200 on any path that isn't under /computeMetadata/v1 with Metadata-Flavor: Google. A 200 here is therefore a strong positive signal that this is Azure IMDS.
type GCPProvider ¶
type GCPProvider struct {
// contains filtered or unexported fields
}
GCPProvider implements Provider against the GCE metadata server.
func NewGCP ¶
func NewGCP() *GCPProvider
NewGCP returns a GCP provider pointed at the link-local IP 169.254.169.254 (see gcpDefaultBase for why we use the IP directly rather than the canonical metadata.google.internal hostname).
func NewGCPWithBase ¶
func NewGCPWithBase(base string) *GCPProvider
NewGCPWithBase constructs a GCP provider against an arbitrary base URL. Used by tests.
func (*GCPProvider) Fetch ¶
func (p *GCPProvider) Fetch(ctx context.Context) (*Metadata, error)
Fetch retrieves the full metadata snapshot. Each sub-fetch is best-effort — failures leave the corresponding Metadata field empty rather than aborting the whole call, since GCE returns 404 (not 500) for genuinely-missing fields.
func (*GCPProvider) Probe ¶
func (p *GCPProvider) Probe(ctx context.Context) bool
Probe reads the instance-id endpoint. A successful 200 with the Metadata-Flavor: Google response header (validated inside get) is the strongest signal that this is GCE — bare 200s without the header are rejected as not-GCP (e.g. a captive portal returning a generic 200).
type Metadata ¶
type Metadata struct {
Provider string
InstanceID string
Region string
AvailabilityZone string
InstanceType string
NetworkID string
AccountID string
}
Metadata is the provider-agnostic snapshot of cloud-instance attributes that reconcile uses. Fields are best-effort: providers that cannot fetch a particular field leave it empty, and ToTags omits empty values.
type Provider ¶
type Provider interface {
Name() string
Probe(ctx context.Context) bool
Fetch(ctx context.Context) (*Metadata, error)
}
Provider is the cloud-specific IMDS client.
Probe is a short-timeout reachability check (typically the cheapest GET that proves the IMDS endpoint is the right provider). Fetch follows up with the full metadata read. Once Probe returns true, the host is treated as being on that provider — Detect never falls back to a different provider on Fetch failure, because IMDS endpoints are provider-specific and partial data is strictly better than wrong-provider data.
func DefaultProviders ¶
func DefaultProviders() []Provider
DefaultProviders returns the production provider list in detect priority.