Documentation
¶
Index ¶
- Constants
- Variables
- func ProviderBody(subscriptionID, namespace, registrationState string, types []ResourceTypeDef) map[string]any
- func RequestHostFromContext(ctx context.Context) string
- func SubscriptionBody(subscriptionID, tenantID, displayName string) map[string]any
- func TenantBody(tenantID string) map[string]any
- func WithRequestHost(ctx context.Context, host string) context.Context
- type ConfigProvider
- type EnvironmentAuth
- type EnvironmentDescriptor
- type EnvironmentSuffixes
- type Handler
- func (h *Handler) ExtractOperation(c *echo.Context) string
- func (h *Handler) ExtractResource(c *echo.Context) string
- func (h *Handler) GetSupportedOperations() []string
- func (h *Handler) Handler() echo.HandlerFunc
- func (h *Handler) MatchPriority() int
- func (h *Handler) Name() string
- func (h *Handler) Reset()
- func (h *Handler) Restore(ctx context.Context, data []byte) error
- func (h *Handler) RouteMatcher() service.Matcher
- func (h *Handler) Shutdown(ctx context.Context)
- func (h *Handler) Snapshot(ctx context.Context) []byte
- func (h *Handler) StartWorker(ctx context.Context) error
- type InMemoryBackend
- func (b *InMemoryBackend) DeleteResourceGroup(name string) error
- func (b *InMemoryBackend) GetResourceGroup(name string) (ResourceGroup, error)
- func (b *InMemoryBackend) IsProviderRegistered(subscriptionID, namespace string) bool
- func (b *InMemoryBackend) ListResourceGroups() []ResourceGroup
- func (b *InMemoryBackend) ProviderRegistrationState(subscriptionID, namespace string) string
- func (b *InMemoryBackend) PutResourceGroup(name, location string, tags map[string]string) (ResourceGroup, bool)
- func (b *InMemoryBackend) RegisterProvider(subscriptionID, namespace string)
- func (b *InMemoryBackend) Reset()
- func (b *InMemoryBackend) ResourceGroupExists(name string) bool
- func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error
- func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte
- type Provider
- type Registry
- func (r *Registry) Delete(ctx context.Context, id ResourceID) error
- func (r *Registry) DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
- func (r *Registry) Get(ctx context.Context, id ResourceID) (map[string]any, error)
- func (r *Registry) List(ctx context.Context, id ResourceID) ([]map[string]any, error)
- func (r *Registry) ListKeys(ctx context.Context, id ResourceID) (map[string]any, error)
- func (r *Registry) ProviderNamed(ns string) (ResourceProvider, bool)
- func (r *Registry) Providers() []string
- func (r *Registry) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, bool, error)
- func (r *Registry) Register(p ResourceProvider)
- func (r *Registry) ResetAll()
- func (r *Registry) SetServiceBusEntities(e ServiceBusEntities)
- type Resource
- type ResourceGroup
- type ResourceID
- type ResourceProvider
- type ResourceTypeDef
- type ServiceBusEndpointConfig
- type ServiceBusEntities
- type ServiceBusPortProvider
- type ServiceBusProvider
- func (p *ServiceBusProvider) Delete(ctx context.Context, id ResourceID) error
- func (p *ServiceBusProvider) DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
- func (p *ServiceBusProvider) Get(_ context.Context, id ResourceID) (map[string]any, error)
- func (p *ServiceBusProvider) List(_ context.Context, id ResourceID) ([]map[string]any, error)
- func (p *ServiceBusProvider) ListKeys(_ context.Context, id ResourceID) (map[string]any, error)
- func (p *ServiceBusProvider) Namespace() string
- func (p *ServiceBusProvider) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, error)
- func (p *ServiceBusProvider) Reset()
- func (p *ServiceBusProvider) ResourceTypes() []ResourceTypeDef
- type Settings
- type StorageAccounts
- type StorageAccountsProvider
- type StorageEndpointConfig
- type StorageProvider
- func (p *StorageProvider) Delete(ctx context.Context, id ResourceID) error
- func (p *StorageProvider) DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
- func (p *StorageProvider) Get(_ context.Context, id ResourceID) (map[string]any, error)
- func (p *StorageProvider) List(_ context.Context, id ResourceID) ([]map[string]any, error)
- func (p *StorageProvider) ListKeys(_ context.Context, id ResourceID) (map[string]any, error)
- func (p *StorageProvider) Namespace() string
- func (p *StorageProvider) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, error)
- func (p *StorageProvider) Reset()
- func (p *StorageProvider) ResourceTypes() []ResourceTypeDef
- type TokenResponse
- type VHostPortProvider
Constants ¶
const DefaultClientID = "00000000-0000-0000-0000-000000000000"
DefaultClientID is the fixed dev client (application) ID.
const DefaultClientSecret = "gopherstack"
DefaultClientSecret is the fixed dev client secret accepted by the token endpoint when validation is off (the default).
const DefaultEnvironmentName = "gopherstack"
DefaultEnvironmentName is the environment name advertised by the metadata document; must equal the Terraform provider's own `environment` setting.
const DefaultLocation = "local"
DefaultLocation is the default Azure "location" value used when a request doesn't specify one.
const DefaultPort = 10006
DefaultPort is the fixed TCP port for the dedicated ARM listener. It sits inside --port-range-start/--port-range-end's default range (10000-10100) and leaves Key Vault (10004, M11) and App Configuration (10005, M12) untouched -- see AZURE.md section 10.7.
const DefaultServiceBusPort = 10003
DefaultServiceBusPort is the default port ARM's Microsoft.ServiceBus RP advertises as serviceBusEndpoint (AZURE.md section 10.10's M9 entry). Mirrors DefaultStorageVHostPort's rationale for duplicating rather than importing services/azureservicebus.DefaultPort: this package has no other reason to depend on that package (rp_servicebus.go delegates through the narrow ServiceBusEntities interface, not a direct import), and importing it just for one int constant would be a needless coupling.
const DefaultStorageVHostPort = 10010
DefaultStorageVHostPort is the default port ARM's Microsoft.Storage RP advertises in primaryEndpoints (AZURE.md section 10.4/10.8). This intentionally duplicates services/azurestoragevhost.DefaultPort as a literal constant rather than importing that package: services/azurearm has no other reason to depend on it (no delegation happens here, see rp_storage.go), and importing a package just for one int constant would be a needless coupling for a documented, stable port number. Unlike the three separate per-service ports this replaced, there is only one port now: terraform-provider-azurerm's data-plane SDK requires Blob/Queue/Table to share a single domain suffix, which (since Go's url.URL.Host always includes the port) forces them onto a single shared port too -- see services/azurestoragevhost's package doc comment for the full derivation.
const DefaultSubscriptionID = "00000000-0000-0000-0000-000000000000"
DefaultSubscriptionID is the fixed dev subscription ID.
const DefaultTenantID = "00000000-0000-0000-0000-000000000000"
DefaultTenantID is the fixed dev AAD tenant ID. It, DefaultSubscriptionID, and DefaultClientID are all-zeros GUIDs, matching LocalStack's own Azure emulator convention: valid GUIDs (azurerm parses subscription_id/tenant_id as GUIDs) that are obviously not real Azure identifiers. See AZURE.md section 10.5.
Variables ¶
var ( ErrInvalidResourceID = errors.New("azurearm: invalid resource ID") ErrResourceGroupNotFound = errors.New("azurearm: resource group not found") ErrResourceNotFound = errors.New("azurearm: resource not found") ErrSubscriptionNotFound = errors.New("azurearm: subscription not found") ErrProviderNotFound = errors.New("azurearm: resource provider not found") ErrInvalidRequestBody = errors.New("azurearm: invalid request body") ErrStorageAccountNotFound = errors.New("azurearm: storage account not found") ErrServiceBusNamespaceNotFound = errors.New("azurearm: service bus namespace not found") ErrServiceBusQueueNotFound = errors.New("azurearm: service bus queue not found") ErrServiceBusTopicNotFound = errors.New("azurearm: service bus topic not found") ErrServiceBusSubscriptionNotFound = errors.New("azurearm: service bus subscription not found") // ErrSnapshotResourceGroupNull/ErrSnapshotResourceNull are returned by // Restore when a persisted snapshot's map holds a JSON null entry, which // decodes to a nil pointer that would panic on first dereference if // stored as-is (same class of bug services/cosmosdb's persistence.go // guards against). ErrSnapshotResourceGroupNull = errors.New("azurearm: restore snapshot: resource group is null") ErrSnapshotResourceNull = errors.New("azurearm: restore snapshot: resource is null") )
Sentinel errors for Azure Resource Manager (ARM) emulation. Mapped to ARM error codes/HTTP statuses in the errorDetails table (handler.go), modeled on services/sqs's errorDetails pattern.
var ErrNilAppContext = errors.New("azurearm: nil app context")
ErrNilAppContext is returned when Init is called with a nil AppContext.
var ErrServiceBusPortMismatch = errors.New(
"azurearm: --azure-arm-servicebus-port must match --azure-servicebus-port",
)
ErrServiceBusPortMismatch is returned when Settings.ServiceBusPort (what ARM advertises as serviceBusEndpoint) disagrees with the actual services/azureservicebus listener's own configured port -- the same fail-fast rationale as ErrStorageVHostPortMismatch.
var ErrStorageVHostPortMismatch = errors.New(
"azurearm: --azure-arm-storage-vhost-port must match --azure-storage-vhost-port",
)
ErrStorageVHostPortMismatch is returned when Settings.StorageVHostPort (what ARM advertises in primaryEndpoints) disagrees with the actual services/azurestoragevhost listener's own configured port. Failing fast here, rather than letting the two silently diverge, avoids repeating the exact "advertised a port nothing answers on" bug class already found and fixed twice in this milestone (AZURE.md section 10.10, M8 bugs (1)/(5)).
Functions ¶
func ProviderBody ¶
func ProviderBody(subscriptionID, namespace, registrationState string, types []ResourceTypeDef) map[string]any
ProviderBody returns the wire response body for GET /subscriptions/{sub}/providers/{ns} and each entry of the GET /subscriptions/{sub}/providers list.
func RequestHostFromContext ¶
RequestHostFromContext returns the host stashed by WithRequestHost, or "" if none was set (e.g. a unit test calling a ResourceProvider directly).
func SubscriptionBody ¶
SubscriptionBody returns the wire response body for GET /subscriptions/{sub} and as the single entry of GET /subscriptions' "value" array. tenantID is a separate value from subscriptionID -- both default to the same fixed all-zeros GUID (AZURE.md section 10.5), but Settings allows overriding either independently, so this must not assume they're equal.
func TenantBody ¶
TenantBody returns the wire response body for one entry of GET /tenants' "value" array.
Types ¶
type ConfigProvider ¶
type ConfigProvider interface {
GetAzureARMSettings() Settings
}
ConfigProvider is a private interface to extract ARM configuration from the abstract AppContext Config, mirroring services/cosmosdb.ConfigProvider.
type EnvironmentAuth ¶
type EnvironmentAuth struct {
LoginEndpoint string `json:"loginEndpoint"`
IdentityProvider string `json:"identityProvider"`
Tenant string `json:"tenant"`
Audiences []string `json:"audiences"`
}
EnvironmentAuth is EnvironmentDescriptor's "authentication" field.
Tenant must be the literal string "common", and IdentityProvider must be "AAD" -- not gopherstack's fixed tenant GUID. hashicorp/go-azure-sdk's Environment.IsAzureStack() (sdk/environments/azure_stack.go) treats any other combination as an Azure Stack environment, which terraform-provider-azurerm explicitly refuses to run against (internal/clients/builder.go: "does not support Azure Stack"). Real Azure's own AzurePublic() environment (sdk/environments/azure_public.go) hardcodes exactly these two values even though it obviously isn't single-tenant -- this is a control field for auth flow selection, not a place to plug in a real tenant ID.
type EnvironmentDescriptor ¶
type EnvironmentDescriptor struct {
Suffixes EnvironmentSuffixes `json:"suffixes"`
Gallery string `json:"gallery"`
Media string `json:"media,omitempty"`
Graph string `json:"graph"`
GraphAudience string `json:"graphAudience"`
Name string `json:"name"`
ResourceManager string `json:"resourceManager"`
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
ActiveDirectoryDataLake string `json:"activeDirectoryDataLake,omitempty"`
SQLManagement string `json:"sqlManagement,omitempty"`
Batch string `json:"batch,omitempty"`
Portal string `json:"portal"`
MicrosoftGraphResourceID string `json:"microsoftGraphResourceId"`
Authentication EnvironmentAuth `json:"authentication"`
}
EnvironmentDescriptor is the GET /metadata/endpoints response body: a SINGLE JSON object, not an array (see BuildMetadataEndpoints's doc comment for why this is non-obvious and easy to get backwards). Field set and semantics verified against hashicorp/go-azure-sdk's environments.FromEndpoint, which hard-fails on a document missing Name, ResourceManagerEndpoint, or MicrosoftGraphResourceID (AZURE.md section 10.8) -- every one of those three, plus every other field FromEndpoint reads, is populated below.
MicrosoftGraphResourceID is a TOP-LEVEL field ("microsoftGraphResourceId"), not nested under a "resourceIdentifiers" object -- verified against go-azure-sdk's sdk/internal/metadata/client.go's unexported metaDataResponse wire struct, which has no ResourceIdentifiers field at all. Nesting it silently zero-values the field on unmarshal (FromEndpoint then fails with "no `microsoftGraphResourceId` was returned").
func BuildMetadataEndpoints ¶
func BuildMetadataEndpoints(baseURL string, settings Settings) EnvironmentDescriptor
BuildMetadataEndpoints builds the GET /metadata/endpoints response body for settings.Environment, with every URL pointing back at baseURL (the ARM listener's own scheme://host:port, e.g. "https://host:10006").
Returns a SINGLE EnvironmentDescriptor, not a slice -- deliberately, and non-obviously so. Real Azure's public /metadata/endpoints does return a JSON array (the multi-cloud discovery list: public/US Gov/China/etc), and that public shape is what most docs and examples show. But terraform-provider-azurerm's metadata_host custom-environment path never hits that public endpoint or its array-shaped client: it goes through hashicorp/go-azure-sdk's environments.FromEndpoint -> sdk/internal/metadata/client.go's GetMetaData, which does `json.Unmarshal(respBody, &metadata)` into a single `*metaDataResponse` struct. An array response there fails with "json: cannot unmarshal array into Go value of type metadata.metaDataResponse" -- a real bug M7 shipped with, undetected because no `terraform apply` had ever reached this endpoint until M8's CI-discovery-glob fix let the Terraform suite actually run (AZURE.md section 10.8 records this). Do not "fix" this back to an array without re-reading that section first.
type EnvironmentSuffixes ¶
type EnvironmentSuffixes struct {
Storage string `json:"storage"`
KeyVaultDNS string `json:"keyVaultDns"`
SQLServerHostname string `json:"sqlServerHostname"`
ACRLoginServer string `json:"acrLoginServer"`
AzureDatalakeStore string `json:"azureDataLakeStoreFileSystem,omitempty"`
}
EnvironmentSuffixes is EnvironmentDescriptor's "suffixes" field.
type Handler ¶
type Handler struct {
Backend *InMemoryBackend
Registry *Registry
Issuer *aadauth.Issuer
Settings Settings
Port int
// contains filtered or unexported fields
}
Handler is the Echo HTTP handler for the ARM emulation's dedicated listener.
func NewHandler ¶
func NewHandler(backend *InMemoryBackend, registry *Registry, issuer *aadauth.Issuer, settings Settings) *Handler
NewHandler creates a new ARM Handler.
func (*Handler) ExtractOperation ¶
ExtractOperation extracts the ARM operation name from the request, for metrics labeling.
func (*Handler) ExtractResource ¶
ExtractResource extracts a resource identifier from the request path, for metrics labeling.
func (*Handler) GetSupportedOperations ¶
GetSupportedOperations returns the list of supported ARM operations.
func (*Handler) Handler ¶
func (h *Handler) Handler() echo.HandlerFunc
Handler returns the Echo handler function for ARM operations.
func (*Handler) MatchPriority ¶
MatchPriority returns the routing priority for the AzureARM handler. Irrelevant in practice since RouteMatcher never matches; 0 is the safe default.
func (*Handler) RouteMatcher ¶
RouteMatcher exists only to satisfy service.Registerable's interface contract: AzureARM deliberately never matches on the shared AWS single-port Router, exactly like services/azureblob/azurequeue/azuretable/ cosmosdb -- it runs on its own dedicated HTTPS listener started by StartWorker. Only RouteMatcher itself is inert.
func (*Handler) Shutdown ¶
Shutdown stops the dedicated ARM listener, mirroring services/azureblob.Handler.Shutdown's graceful-then-forced-close shape.
func (*Handler) Snapshot ¶
Snapshot implements persistence.Persistable by delegating to InMemoryBackend only -- the registry's Storage RP is not yet included, see armSnapshot's doc comment.
func (*Handler) StartWorker ¶
StartWorker binds AzureARM's dedicated fixed port and serves HTTPS -- either with a stable caller-supplied certificate or a freshly generated self-signed one (see loadOrGenerateCert) -- synchronously, failing fast if the port is unavailable rather than falling back into the shared PortAlloc pool -- exactly like services/azureblob/azurequeue/azuretable/cosmosdb's StartWorker (see AZURE.md section 10.7). Unlike those, ARM serves HTTPS unconditionally: azurerm's metadata_host handling hardcodes "https://" (AZURE.md section 10.8), so a plain-HTTP listener here would make provider initialization fail outright, not merely warn.
type InMemoryBackend ¶
type InMemoryBackend struct {
// contains filtered or unexported fields
}
InMemoryBackend owns the ARM resource graph: one subscription's resource groups, its generic resource-provider-registration state, and the flat map of every resource not owned by a dedicated ResourceProvider's own internal state (e.g. rp_storage.go keeps its own storage-account map -- see registry.go for how the two interact). This is ARM-side metadata ONLY, never a second copy of any data-plane service's state (AZURE.md section 10.9).
func NewInMemoryBackend ¶
func NewInMemoryBackend() *InMemoryBackend
NewInMemoryBackend creates an empty InMemoryBackend.
func (*InMemoryBackend) DeleteResourceGroup ¶
func (b *InMemoryBackend) DeleteResourceGroup(name string) error
DeleteResourceGroup deletes the resource group named name, along with every generic resource whose ResourceGroup matches (case-insensitively). Resources owned by a dedicated ResourceProvider's own internal state (e.g. storage accounts) are NOT cascaded here -- see registry.go's DeleteResourcesInGroup, which handler.go calls across every registered provider before calling this.
func (*InMemoryBackend) GetResourceGroup ¶
func (b *InMemoryBackend) GetResourceGroup(name string) (ResourceGroup, error)
GetResourceGroup returns the resource group named name.
func (*InMemoryBackend) IsProviderRegistered ¶
func (b *InMemoryBackend) IsProviderRegistered(subscriptionID, namespace string) bool
IsProviderRegistered reports whether namespace has been registered for subscriptionID.
func (*InMemoryBackend) ListResourceGroups ¶
func (b *InMemoryBackend) ListResourceGroups() []ResourceGroup
ListResourceGroups returns every resource group, sorted by name.
func (*InMemoryBackend) ProviderRegistrationState ¶
func (b *InMemoryBackend) ProviderRegistrationState(subscriptionID, namespace string) string
ProviderRegistrationState returns "Registered" or "NotRegistered" for namespace under subscriptionID, the value ARM's /providers/{ns} response carries in its "registrationState" field.
func (*InMemoryBackend) PutResourceGroup ¶
func (b *InMemoryBackend) PutResourceGroup(name, location string, tags map[string]string) (ResourceGroup, bool)
PutResourceGroup creates or updates the resource group named name. Always synchronous (AZURE.md section 10.3): the caller determines 200 vs 201 from the returned created bool.
func (*InMemoryBackend) RegisterProvider ¶
func (b *InMemoryBackend) RegisterProvider(subscriptionID, namespace string)
RegisterProvider marks namespace as registered for subscriptionID.
func (*InMemoryBackend) Reset ¶
func (b *InMemoryBackend) Reset()
Reset clears all in-memory ARM state. Used by the POST /_gopherstack/reset endpoint. Never touches any data-plane service's own state (AZURE.md section 10.9).
func (*InMemoryBackend) ResourceGroupExists ¶
func (b *InMemoryBackend) ResourceGroupExists(name string) bool
ResourceGroupExists reports whether a resource group named name exists.
func (*InMemoryBackend) Restore ¶
func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error
Restore loads backend state from a JSON snapshot. It implements persistence.Persistable. Restore must be idempotent against the data plane (AZURE.md section 10.9): this method only ever restores ARM's own metadata, never re-creates or otherwise touches any data-plane service's state.
type Provider ¶
type Provider struct{}
Provider implements service.Provider for the ARM emulation.
Like services/azureblob/azurequeue/azuretable/cosmosdb/azureservicebus, AzureARM does not register a RouteMatcher into the shared AWS single-port Router -- see handler.go's RouteMatcher doc comment. It is registered in cli.go's getMostRecentServiceProviders like every other provider; only its RouteMatcher (which always returns false) is inert.
func (*Provider) Init ¶
func (p *Provider) Init(ctx *service.AppContext) (service.Registerable, error)
Init initializes the ARM backend, registry (with the Storage RP registered), AAD token issuer, and handler. The configured port (Settings.Port, default DefaultPort) is only recorded here; the actual TCP bind happens synchronously in Handler.StartWorker, so a port-in-use failure is returned to the caller directly instead of being discovered later from a background goroutine.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry dispatches generic-resource operations to the ResourceProvider registered for a namespace, falling back to InMemoryBackend's own generic (metadata-only) resource storage for any namespace with no dedicated provider -- so PUT/GET/DELETE of an arbitrary Microsoft.SomeFutureThing/whatever resource still round-trips instead of 404ing, per AZURE.md section 10.1's generic-resource-plane requirement.
func NewRegistry ¶
func NewRegistry(backend *InMemoryBackend) *Registry
NewRegistry creates a Registry backed by backend, with no providers registered yet (use Register).
func (*Registry) Delete ¶
func (r *Registry) Delete(ctx context.Context, id ResourceID) error
Delete removes the resource identified by id.
func (*Registry) DeleteResourcesInGroup ¶
DeleteResourcesInGroup cascades a resource-group delete into every registered dedicated provider, so a provider's own resources in that group (e.g. StorageProvider's accounts) don't survive as orphans reachable by their own resource ID after the owning group is gone.
func (*Registry) ProviderNamed ¶
func (r *Registry) ProviderNamed(ns string) (ResourceProvider, bool)
ProviderNamed returns the dedicated ResourceProvider registered for ns (matched case-insensitively, AZURE.md section 10.1), and whether one was found. Used by the /providers/{ns} and /providers/{ns}/register handlers, which take ns directly from the URL path rather than a parsed ResourceID.
func (*Registry) Providers ¶
Providers returns the namespaces of every registered dedicated ResourceProvider, sorted, for the provider-list endpoint.
func (*Registry) Put ¶
func (r *Registry) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, bool, error)
Put creates or updates the resource identified by id.
func (*Registry) Register ¶
func (r *Registry) Register(p ResourceProvider)
Register adds a ResourceProvider to the registry, keyed by its lowercased Namespace() -- ARM namespaces are matched case-insensitively (AZURE.md section 10.1: "Microsoft.storage" and "microsoft.STORAGE" must both reach the same dedicated provider, not fall through to the generic pass-through).
func (*Registry) ResetAll ¶
func (r *Registry) ResetAll()
ResetAll clears every registered dedicated provider's own state. Called by Handler.Reset alongside InMemoryBackend.Reset -- the generic pass-through resources and dedicated-provider resources (e.g. StorageProvider.accounts) are two separate stores, and the /_gopherstack/reset endpoint must clear both.
func (*Registry) SetServiceBusEntities ¶
func (r *Registry) SetServiceBusEntities(e ServiceBusEntities)
SetServiceBusEntities gives the registered Microsoft.ServiceBus provider (if any) a real ServiceBusEntities adapter, replacing its no-op default -- called post-construction by cli.go's wireAzureARMResourceProviders once services/azureservicebus's own Handler exists, mirroring M8's wireAzureStorageVHost pattern (this can't be wired at azurearm.Provider.Init time the way StorageAccountsProvider is, since it needs a live reference to a sibling service's already-constructed runtime handler). A no-op if Microsoft.ServiceBus isn't registered or isn't a *ServiceBusProvider.
type Resource ¶
type Resource struct {
Tags map[string]string
Properties map[string]any
SKU map[string]any
Location string
Kind string
ID ResourceID
}
Resource is the generic stored representation of an ARM resource (any namespace/type not owned by a specific ResourceProvider -- see registry.go's fallback behavior). ResourceProvider implementations (e.g. rp_storage.go) may keep their own richer internal representation and build the wire body directly instead of using this type; Resource exists for the generic pass-through path every namespace gets even without a dedicated RP.
type ResourceGroup ¶
type ResourceGroup struct {
Tags map[string]string `json:"tags,omitempty"`
Name string `json:"name"`
Location string `json:"location"`
}
ResourceGroup is the stored representation of an ARM resource group.
type ResourceID ¶
type ResourceID struct {
SubscriptionID string
ResourceGroup string
Namespace string
Types []string
Names []string
}
ResourceID is a parsed ARM resource identifier of the generic shape /subscriptions/{sub}/resourceGroups/{rg}/providers/{namespace}/{type1}/{name1}[/{type2}/{name2}...].
Types/Names are parallel slices so nested child resources (e.g. Microsoft.ServiceBus/namespaces/{ns}/queues/{q}, two type/name pairs) are represented without a separate "parent" concept -- exactly the single generic path walker AZURE.md section 10.1 requires instead of per-type route registration.
func ParseGenericResourceListPath ¶
func ParseGenericResourceListPath(path string) (ResourceID, bool, error)
ParseGenericResourceListPath parses a resource-type LIST path, either scoped to a resource group:
/subscriptions/{sub}/resourceGroups/{rg}/providers/{ns}/{type}
or across the whole subscription:
/subscriptions/{sub}/providers/{ns}/{type}
Returns the parsed (partial) ResourceID -- Names is empty -- and whether a resource group scope was present.
func ParseGenericResourcePath ¶
func ParseGenericResourcePath(path string) (ResourceID, error)
ParseGenericResourcePath parses a full generic-resource ARM path:
/subscriptions/{sub}/resourceGroups/{rg}/providers/{ns}/{type}/{name}[/{type}/{name}...]
The "resourcegroups"/"resourceGroups" segment is matched case-insensitively per AZURE.md section 10.1 -- real ARM accepts both and azurerm emits both depending on code path.
func (ResourceID) ARMID ¶
func (id ResourceID) ARMID() string
ARMID renders the canonical ARM resource ID string for id, always using the canonical "resourceGroups" casing regardless of what was parsed.
func (ResourceID) LeafName ¶
func (id ResourceID) LeafName() string
LeafName returns the last (innermost) resource name segment -- the name of the resource this ID actually identifies.
func (ResourceID) LeafType ¶
func (id ResourceID) LeafType() string
LeafType returns the last (innermost) resource type segment.
func (ResourceID) ResourceType ¶
func (id ResourceID) ResourceType() string
ResourceType returns the ARM resource type string, e.g. "Microsoft.Storage/storageAccounts" or "Microsoft.ServiceBus/namespaces/queues".
type ResourceProvider ¶
type ResourceProvider interface {
// Namespace returns the ARM provider namespace this implementation
// serves, e.g. "Microsoft.Storage".
Namespace() string
// ResourceTypes returns the resource types this provider serves, for
// the /providers/{ns} registration-status response.
ResourceTypes() []ResourceTypeDef
// Put creates or updates the resource identified by id from body,
// returning the full wire response body.
Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, error)
// Get returns the full wire response body for id.
Get(ctx context.Context, id ResourceID) (map[string]any, error)
// Delete removes the resource identified by id.
Delete(ctx context.Context, id ResourceID) error
// List returns every resource of id's LeafType, scoped to id's
// ResourceGroup if set, else the whole subscription.
List(ctx context.Context, id ResourceID) ([]map[string]any, error)
// ListKeys returns the POST .../listKeys response body for id.
ListKeys(ctx context.Context, id ResourceID) (map[string]any, error)
// Reset clears all of this provider's own state, called by
// Handler.Reset via Registry.ResetAll (the /_gopherstack/reset
// endpoint) -- without this, a dedicated provider's resources survive
// a reset that's supposed to wipe every service's state.
Reset()
// DeleteResourcesInGroup deletes every resource this provider owns in
// resourceGroup, called by Registry.DeleteResourcesInGroup when a
// resource group is deleted -- without this, a dedicated provider's
// resources in that group are orphaned (InMemoryBackend's own generic
// resources are cascaded directly by DeleteResourceGroup, but a
// dedicated provider's internal state is opaque to InMemoryBackend).
DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
}
ResourceProvider is the small interface a resource-provider namespace (Microsoft.Storage today; Microsoft.ServiceBus/Microsoft.DocumentDB in M8/M9, Microsoft.KeyVault/Microsoft.AppConfiguration in M11/M12) plugs into the registry with. Deliberately narrow (AZURE.md section 10.6) so each future milestone adds one file and one registry entry.
Put/Get/Delete/ListKeys all return the full ARM wire response body (or nil for Delete's 204 case) -- the ResourceProvider owns its own response shape entirely, including the common id/name/type/location/tags/ properties.provisioningState envelope (Resource.Body / a hand-built equivalent), rather than the registry re-wrapping it.
type ResourceTypeDef ¶
ResourceTypeDef describes one resource type a ResourceProvider serves -- its type name, supported API versions, and whether it has child resource types nested beneath it (informational only in this MVP; see registry.go).
type ServiceBusEndpointConfig ¶
ServiceBusEndpointConfig configures how the ServiceBus resource provider advertises the namespace's serviceBusEndpoint. Unlike Storage's StorageEndpointConfig, this needs no vhost/shared-port scheme: services/azureservicebus has no namespace-in-URL routing at all (a flat host:port reaches it directly), so a single scalar override suffices.
type ServiceBusEntities ¶
type ServiceBusEntities interface {
CreateQueue(name string, lockDuration, defaultMessageTTL time.Duration, maxDeliveryCount int) error
DeleteQueue(name string) error
QueueExists(name string) bool
CreateTopic(name string, defaultMessageTTL time.Duration) error
DeleteTopic(name string) error
TopicExists(name string) bool
CreateSubscription(topic, name string, lockDuration time.Duration, maxDeliveryCount int) error
DeleteSubscription(topic, name string) error
SubscriptionExists(topic, name string) bool
}
ServiceBusEntities is the narrow, consumer-defined seam the Microsoft.ServiceBus resource provider (rp_servicebus.go) delegates queue/topic/subscription lifecycle through, mirroring StorageAccounts's shape above. Uses only primitive types (time.Duration, int, string) rather than services/azureservicebus's own EntityConfig, so this package never imports a data-plane service package (AZURE.md section 10.6) -- the real adapter translating these calls into azureservicebus.EntityConfig lives in cli_adapters.go, which already imports across services.
Unlike StorageAccounts, ServiceBusEntities calls are load-bearing for M9's test plan: services/azureservicebus (M5) is one-process-one-namespace with no ARM-shaped state of its own, so a Terraform-created queue/topic/ subscription must actually exist in the real M5 backend for the REST send->peek-lock->complete liveness round-trip to succeed -- this is not an optional best-effort hook the way Storage's RegisterAccount is.
type ServiceBusPortProvider ¶
type ServiceBusPortProvider interface {
GetAzureServiceBusPort() int
}
ServiceBusPortProvider is a private interface AppContext.Config may implement to expose the actual services/azureservicebus listener's configured port, so Init can cross-check it against Settings.ServiceBusPort. cli.go implements this via CLI.GetAzureServiceBusPort.
type ServiceBusProvider ¶
type ServiceBusProvider struct {
// contains filtered or unexported fields
}
ServiceBusProvider implements ResourceProvider for Microsoft.ServiceBus. Unlike StorageProvider, its queue/topic/subscription Put calls ARE load-bearing against dataPlane (services/azureservicebus, M5) -- M5 has no ARM-shaped state of its own, so a Terraform-created entity must genuinely exist in the real backend for AZURE.md's M9 test plan (a REST send->peek-lock->complete round-trip) to succeed. Namespace Put is metadata-only: M5 is one-process-one-namespace, so there's nothing to delegate for "creating" one.
func NewServiceBusProvider ¶
func NewServiceBusProvider(cfg ServiceBusEndpointConfig, dataPlane ServiceBusEntities) *ServiceBusProvider
NewServiceBusProvider creates a ServiceBusProvider. dataPlane may be nil, in which case a no-op default is used (see interfaces.go) until cli.go's wireAzureARMResourceProviders wires a real one post-construction.
func (*ServiceBusProvider) Delete ¶
func (p *ServiceBusProvider) Delete(ctx context.Context, id ResourceID) error
Delete implements ResourceProvider.
func (*ServiceBusProvider) DeleteResourcesInGroup ¶
func (p *ServiceBusProvider) DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
DeleteResourcesInGroup implements ResourceProvider (Registry's cascade delete when a resource group is deleted).
func (*ServiceBusProvider) Get ¶
func (p *ServiceBusProvider) Get(_ context.Context, id ResourceID) (map[string]any, error)
Get implements ResourceProvider.
func (*ServiceBusProvider) List ¶
func (p *ServiceBusProvider) List(_ context.Context, id ResourceID) ([]map[string]any, error)
List implements ResourceProvider, scoped to id.ResourceGroup if set, else every resource of id's LeafType in the subscription.
func (*ServiceBusProvider) ListKeys ¶
func (p *ServiceBusProvider) ListKeys(_ context.Context, id ResourceID) (map[string]any, error)
ListKeys implements ResourceProvider for namespaces/{ns}/authorizationRules/{rule}/listKeys. Response shape -- {"primaryConnectionString","secondaryConnectionString","primaryKey", "secondaryKey","keyName"} -- matches real ARM's Service Bus AuthorizationRules ListKeys AccessKeys model. This emulator has exactly one fixed dev SAS identity (services/azureservicebus/sas.go's DefaultKeyName/DefaultKeyValue, mirrored here as sbDefaultKeyName/ sbDefaultKeyValue), so both primary/secondary key are the same value -- matching StorageProvider.ListKeys's identical key1/key2 convention.
func (*ServiceBusProvider) Namespace ¶
func (p *ServiceBusProvider) Namespace() string
Namespace implements ResourceProvider.
func (*ServiceBusProvider) Put ¶
func (p *ServiceBusProvider) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, error)
Put implements ResourceProvider.
func (*ServiceBusProvider) Reset ¶
func (p *ServiceBusProvider) Reset()
Reset implements ResourceProvider (Registry.ResetAll, the /_gopherstack/reset endpoint).
func (*ServiceBusProvider) ResourceTypes ¶
func (p *ServiceBusProvider) ResourceTypes() []ResourceTypeDef
ResourceTypes implements ResourceProvider.
type Settings ¶
type Settings struct {
TenantID string `` //nolint:lll // config struct tags are intentionally verbose
/* 227-byte string literal not displayed */
SubscriptionID string `` //nolint:lll // config struct tags are intentionally verbose
/* 192-byte string literal not displayed */
ClientID string `` //nolint:lll // config struct tags are intentionally verbose
/* 198-byte string literal not displayed */
ClientSecret string `` //nolint:lll // config struct tags are intentionally verbose
/* 249-byte string literal not displayed */
Environment string `` //nolint:lll // config struct tags are intentionally verbose
/* 270-byte string literal not displayed */
Location string `` //nolint:lll // config struct tags are intentionally verbose
/* 229-byte string literal not displayed */
AdvertiseStorageVHost string `` //nolint:lll // config struct tags are intentionally verbose
/* 357-byte string literal not displayed */
TLSCertFile string `` //nolint:lll // config struct tags are intentionally verbose
/* 311-byte string literal not displayed */
TLSKeyFile string `` //nolint:lll // config struct tags are intentionally verbose
/* 253-byte string literal not displayed */
AdvertiseServiceBus string `` //nolint:lll // config struct tags are intentionally verbose
/* 290-byte string literal not displayed */
Port int `` //nolint:lll // config struct tags are intentionally verbose
/* 260-byte string literal not displayed */
StorageVHostPort int `` //nolint:lll // config struct tags are intentionally verbose
/* 336-byte string literal not displayed */
ServiceBusPort int `` //nolint:lll // config struct tags are intentionally verbose
/* 323-byte string literal not displayed */
ValidateTokens bool `` //nolint:lll // config struct tags are intentionally verbose
/* 258-byte string literal not displayed */
}
Settings holds service-level configuration for the ARM emulation backend. Fields are picked up by the Kong CLI parser when embedded in the root CLI command (see cli.go's CLI.AzureARM field), mirroring services/cosmosdb's Settings pattern. Field order below is grouped by type (all strings, then all ints, then the bool) rather than by topic, to satisfy govet's fieldalignment check -- interleaving AdvertiseServiceBus (a string) between the int fields, as a topic-grouped order would, widens the struct's leading run of pointer-containing fields from 152 to 168 bytes. Safe to reorder: every construction of Settings (DefaultSettings, and Kong's tag-driven parsing) uses field names, never positional literals.
func DefaultSettings ¶
func DefaultSettings() Settings
DefaultSettings returns the default Settings. Used when no ConfigProvider is available at init time (e.g. tests constructing a Provider directly).
type StorageAccounts ¶
type StorageAccounts interface {
// RegisterAccount is called when the Storage RP creates a new storage
// account. A nil-safe no-op default (noopStorageAccounts) is used when no
// real adapter is wired, so services/azurearm works standalone in unit
// tests and degrades gracefully if the data plane is disabled.
RegisterAccount(name string) error
// DeleteAccount is called when the Storage RP deletes a storage account.
DeleteAccount(name string) error
}
StorageAccounts is the narrow, consumer-defined seam the Microsoft.Storage resource provider (rp_storage.go) delegates account lifecycle events through, mirroring the wireCrossServiceDependencies adapter pattern cli_adapters.go already uses ~60 times for AWS (see AZURE.md section 10.6). It is declared here even though M7's Storage RP does not need it to serve Blob/Queue/Table traffic today (those backends are already account-name-agnostic -- AZURE.md section 10.4) -- so that M10's per-account namespacing (RegisterAccount/DeleteAccount keying each data plane's top-level map by account name) is additive: rp_storage.go already calls these hooks, they're just no-ops until M10 wires a real adapter in cli_adapters.go.
CosmosResources (M10's equivalent) is deliberately NOT declared here yet -- AZURE.md's own scope note prefers keeping this milestone's interface surface to what it actually uses, over speculatively declaring shapes for resource providers this milestone doesn't implement. ServiceBusEntities below is M9's version of the same pattern, now that M9 actually needs it.
type StorageAccountsProvider ¶
type StorageAccountsProvider interface {
GetAzureARMStorageAccounts() StorageAccounts
}
StorageAccountsProvider is a private interface AppContext.Config may implement to supply a real StorageAccounts adapter (wired by cli.go's wireCrossServiceDependencies in a future milestone -- see interfaces.go). Absent (the common case for M7), NewStorageProvider falls back to the nil-safe noopStorageAccounts default.
type StorageEndpointConfig ¶
StorageEndpointConfig configures how the Storage resource provider advertises Blob/Queue/Table data-plane endpoints in properties.primaryEndpoints, per AZURE.md section 10.4's endpoint-advertisement design. VHostOverride/VHostPort replace this struct's earlier three separate per-service overrides/ports (Blob/Queue/Table): terraform-provider-azurerm's data-plane SDK requires Blob/Queue/Table to share one domain suffix for account-ID parsing, and since Go's url.URL.Host always includes the port, that forces them onto one shared port too -- see services/azurestoragevhost's package doc comment and AZURE.md section 10.8 for the full derivation. advertiseVHostEndpoint below is the only thing that reads these.
type StorageProvider ¶
type StorageProvider struct {
// contains filtered or unexported fields
}
StorageProvider implements ResourceProvider for Microsoft.Storage. Per AZURE.md section 10.4, it is metadata-only: Blob/Queue/Table are already account-name-agnostic, so no data-plane delegation is required for an ARM-created account to be immediately usable. StorageAccounts (accounts StorageAccounts interface) is called best-effort for forward-compat with M10's per-account namespacing.
func NewStorageProvider ¶
func NewStorageProvider(cfg StorageEndpointConfig, dataPlane StorageAccounts) *StorageProvider
NewStorageProvider creates a StorageProvider. dataPlane may be nil, in which case a no-op default is used (see interfaces.go).
func (*StorageProvider) Delete ¶
func (p *StorageProvider) Delete(ctx context.Context, id ResourceID) error
Delete implements ResourceProvider.
func (*StorageProvider) DeleteResourcesInGroup ¶
func (p *StorageProvider) DeleteResourcesInGroup(ctx context.Context, resourceGroup string)
DeleteResourcesInGroup implements the cascade-delete hook Registry.DeleteResourcesInGroup calls when a resource group is deleted (CodeRabbit-flagged: DELETE .../resourceGroups/{name} previously only cleared InMemoryBackend's generic resources, leaving StorageProvider accounts in that group orphaned but still reachable).
func (*StorageProvider) Get ¶
func (p *StorageProvider) Get(_ context.Context, id ResourceID) (map[string]any, error)
Get implements ResourceProvider.
func (*StorageProvider) List ¶
func (p *StorageProvider) List(_ context.Context, id ResourceID) ([]map[string]any, error)
List implements ResourceProvider, scoped to id.ResourceGroup if set (per the ResourceProvider interface contract), else every account in the subscription.
func (*StorageProvider) ListKeys ¶
func (p *StorageProvider) ListKeys(_ context.Context, id ResourceID) (map[string]any, error)
ListKeys implements ResourceProvider. The response shape -- {"keys":[{"keyName","value","permissions"}, ...]} -- was verified against the real ARM "Storage Accounts - List Keys" REST API documentation (learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list-keys): StorageAccountListKeysResult.keys is a StorageAccountKey[] with exactly those three fields, "permissions" taking the KeyPermission enum value "Full" (not "FULL"). Key values are pkgs/azureauth's well-known devstoreaccount1 development key, matching every other Azure service's well-known-credential convention.
func (*StorageProvider) Namespace ¶
func (p *StorageProvider) Namespace() string
Namespace implements ResourceProvider.
func (*StorageProvider) Put ¶
func (p *StorageProvider) Put(ctx context.Context, id ResourceID, body map[string]any) (map[string]any, error)
Put implements ResourceProvider: creates or updates a storage account.
func (*StorageProvider) Reset ¶
func (p *StorageProvider) Reset()
Reset implements the reset hook Registry.ResetAll calls across every registered provider (Handler.Reset -- CodeRabbit-flagged: without this, accounts created before a /_gopherstack/reset survived it, since only InMemoryBackend was being cleared).
func (*StorageProvider) ResourceTypes ¶
func (p *StorageProvider) ResourceTypes() []ResourceTypeDef
ResourceTypes implements ResourceProvider.
type TokenResponse ¶
type TokenResponse struct {
TokenType string `json:"token_type"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
TokenResponse is the client-credentials grant response body, for both the v1 (/{tenant}/oauth2/token) and v2 (/{tenant}/oauth2/v2.0/token) endpoints -- AZURE.md section 10.1 specifies the identical body shape for both.
func IssueToken ¶
func IssueToken(issuer *aadauth.Issuer, baseURL, tenant, clientID, scopeOrResource string) (TokenResponse, error)
IssueToken issues a client-credentials access token for tenant, using issuer, returning the wire response body.
type VHostPortProvider ¶
type VHostPortProvider interface {
GetAzureStorageVHostPort() int
}
VHostPortProvider is a private interface AppContext.Config may implement to expose the actual services/azurestoragevhost listener's configured port, so Init can cross-check it against Settings.StorageVHostPort. cli.go implements this via CLI.GetAzureStorageVHostPort.