package hold import ( "context" "encoding/json" "fmt" "net/http" "time" ) // CloudMetadata contains region info from cloud metadata service type CloudMetadata struct { Region string } // metadataEndpoint is the cloud metadata service URL. Package-level var for test override. var metadataEndpoint = "http://169.254.169.254" // DetectCloudMetadata queries the instance metadata service (169.254.169.254) // Currently supports UpCloud. Others can be added via PR. func DetectCloudMetadata(ctx context.Context) (*CloudMetadata, error) { ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel() // Try UpCloud metadata format if meta, err := detectUpCloud(ctx); err == nil { return meta, nil } // Add other providers here (AWS, GCP, Azure, DigitalOcean, etc.) // Contributors welcome! return nil, nil // No metadata available } // detectUpCloud queries UpCloud's metadata service func detectUpCloud(ctx context.Context) (*CloudMetadata, error) { req, err := http.NewRequestWithContext(ctx, "GET", metadataEndpoint+"/metadata/v1.json", nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("metadata returned %d", resp.StatusCode) } var data struct { CloudName string `json:"cloud_name"` Region string `json:"region"` } if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return nil, err } if data.CloudName != "upcloud" { return nil, fmt.Errorf("not upcloud: %s", data.CloudName) } return &CloudMetadata{ Region: data.Region, }, nil }