Summary
Create ProvisioningManager — the extension-side orchestrator that handles gRPC stream lifecycle, message broker setup, handler dispatch, and provider factory invocation. This is the component that runs inside the extension process and bridges extension-authored ProvisioningProvider implementations with azd core requests.
Parent Epic
Part of #7465 — Provisioning Providers in the AZD Extension Framework
Context
Architectural Decision: Single Provider (Not ComponentManager)
Unlike service targets and framework services (which use ComponentManager for per-service instances), provisioning providers are project-level singletons — an extension provides exactly one provisioning provider per registration. The ProvisioningManager holds a single provider instance directly. The factory is called once during onInitialize, and the instance is reused for all subsequent calls.
Patterns to Follow
ServiceTargetManager in pkg/azdext/service_target_manager.go
FrameworkServiceManager in pkg/azdext/framework_service_manager.go
Detailed Requirements
Struct
type ProvisioningManager struct {
extensionId string
client *AzdClient
broker *grpcbroker.MessageBroker[ProvisioningMessage]
provider ProvisioningProvider // Single instance (NOT ComponentManager)
providerName string
brokerLogger *log.Logger
mu sync.RWMutex
}
Core Methods
ensureStream() — Lazy initialization: open gRPC stream via client.Provisioning().Stream(), create MessageBroker with ProvisioningEnvelope, register all handlers via broker.On()
Register(ctx, factory, providerName) — Send RegisterProvisioningProviderRequest with provider name, wait for RegisterProvisioningProviderResponse
Receive(ctx) — Call broker.Run(ctx) as blocking message dispatcher
Ready(ctx) — Call broker.Ready(ctx) for readiness signaling
Handler Methods (registered via broker.On())
Each handler is called when azd core sends the corresponding request through the stream:
onInitialize(ctx, *ProvisioningInitializeRequest) — Create provider via factory, store instance, call provider.Initialize()
onState(ctx, *ProvisioningStateRequest) — Delegate to provider.State()
onDeploy(ctx, *ProvisioningDeployRequest, ProgressFunc) — Delegate to provider.Deploy() with progress callback that sends ProvisioningDeployProgressMessage via broker.Send()
onPreview(ctx, *ProvisioningPreviewRequest, ProgressFunc) — Delegate to provider.Preview() with progress
onDestroy(ctx, *ProvisioningDestroyRequest, ProgressFunc) — Delegate to provider.Destroy() with progress
onEnsureEnv(ctx, *ProvisioningEnsureEnvRequest) — Delegate to provider.EnsureEnv()
onParameters(ctx, *ProvisioningParametersRequest) — Delegate to provider.Parameters()
Progress Forwarding
For Deploy/Preview/Destroy, the handler receives a ProgressFunc from the broker. The manager must create a ProgressReporter that wraps this and calls the extension-authored provider with it, so the provider can report progress that flows back to azd core.
Thread Safety
- Lazy stream initialization via
sync.RWMutex (double-checked locking pattern)
- All broker operations are thread-safe by design
Acceptance Criteria
Dependencies
Files
- Create:
pkg/azdext/provisioning_manager.go
Summary
Create
ProvisioningManager— the extension-side orchestrator that handles gRPC stream lifecycle, message broker setup, handler dispatch, and provider factory invocation. This is the component that runs inside the extension process and bridges extension-authoredProvisioningProviderimplementations with azd core requests.Parent Epic
Part of #7465 — Provisioning Providers in the AZD Extension Framework
Context
Architectural Decision: Single Provider (Not ComponentManager)
Unlike service targets and framework services (which use
ComponentManagerfor per-service instances), provisioning providers are project-level singletons — an extension provides exactly one provisioning provider per registration. TheProvisioningManagerholds a single provider instance directly. The factory is called once duringonInitialize, and the instance is reused for all subsequent calls.Patterns to Follow
ServiceTargetManagerinpkg/azdext/service_target_manager.goFrameworkServiceManagerinpkg/azdext/framework_service_manager.goDetailed Requirements
Struct
Core Methods
ensureStream()— Lazy initialization: open gRPC stream viaclient.Provisioning().Stream(), createMessageBrokerwithProvisioningEnvelope, register all handlers viabroker.On()Register(ctx, factory, providerName)— SendRegisterProvisioningProviderRequestwith provider name, wait forRegisterProvisioningProviderResponseReceive(ctx)— Callbroker.Run(ctx)as blocking message dispatcherReady(ctx)— Callbroker.Ready(ctx)for readiness signalingHandler Methods (registered via
broker.On())Each handler is called when azd core sends the corresponding request through the stream:
onInitialize(ctx, *ProvisioningInitializeRequest)— Create provider via factory, store instance, callprovider.Initialize()onState(ctx, *ProvisioningStateRequest)— Delegate toprovider.State()onDeploy(ctx, *ProvisioningDeployRequest, ProgressFunc)— Delegate toprovider.Deploy()with progress callback that sendsProvisioningDeployProgressMessageviabroker.Send()onPreview(ctx, *ProvisioningPreviewRequest, ProgressFunc)— Delegate toprovider.Preview()with progressonDestroy(ctx, *ProvisioningDestroyRequest, ProgressFunc)— Delegate toprovider.Destroy()with progressonEnsureEnv(ctx, *ProvisioningEnsureEnvRequest)— Delegate toprovider.EnsureEnv()onParameters(ctx, *ProvisioningParametersRequest)— Delegate toprovider.Parameters()Progress Forwarding
For Deploy/Preview/Destroy, the handler receives a
ProgressFuncfrom the broker. The manager must create aProgressReporterthat wraps this and calls the extension-authored provider with it, so the provider can report progress that flows back to azd core.Thread Safety
sync.RWMutex(double-checked locking pattern)Acceptance Criteria
ProvisioningManagerstruct with required fields (no ComponentManager)ensureStream()opens gRPC stream and creates MessageBrokerRegister()sends registration request and waits for responseReceive()startsbroker.Run()as blocking dispatcherReady()callsbroker.Ready()for readiness signalonInitialize()creates provider via factory and callsInitialize()onState()delegates toprovider.State()onDeploy()delegates with progress forwardingonPreview()delegates with progress forwardingonDestroy()delegates with progress forwardingonEnsureEnv()delegates toprovider.EnsureEnv()onParameters()delegates toprovider.Parameters()request_idbroker.On()Dependencies
client.Provisioning())Files
pkg/azdext/provisioning_manager.go