Skip to content

Manager: Implement extension-side ProvisioningManager #7470

Description

@wbreza

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

  1. ensureStream() — Lazy initialization: open gRPC stream via client.Provisioning().Stream(), create MessageBroker with ProvisioningEnvelope, register all handlers via broker.On()
  2. Register(ctx, factory, providerName) — Send RegisterProvisioningProviderRequest with provider name, wait for RegisterProvisioningProviderResponse
  3. Receive(ctx) — Call broker.Run(ctx) as blocking message dispatcher
  4. 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:

  1. onInitialize(ctx, *ProvisioningInitializeRequest) — Create provider via factory, store instance, call provider.Initialize()
  2. onState(ctx, *ProvisioningStateRequest) — Delegate to provider.State()
  3. onDeploy(ctx, *ProvisioningDeployRequest, ProgressFunc) — Delegate to provider.Deploy() with progress callback that sends ProvisioningDeployProgressMessage via broker.Send()
  4. onPreview(ctx, *ProvisioningPreviewRequest, ProgressFunc) — Delegate to provider.Preview() with progress
  5. onDestroy(ctx, *ProvisioningDestroyRequest, ProgressFunc) — Delegate to provider.Destroy() with progress
  6. onEnsureEnv(ctx, *ProvisioningEnsureEnvRequest) — Delegate to provider.EnsureEnv()
  7. 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

  • ProvisioningManager struct with required fields (no ComponentManager)
  • ensureStream() opens gRPC stream and creates MessageBroker
  • Register() sends registration request and waits for response
  • Receive() starts broker.Run() as blocking dispatcher
  • Ready() calls broker.Ready() for readiness signal
  • onInitialize() creates provider via factory and calls Initialize()
  • onState() delegates to provider.State()
  • onDeploy() delegates with progress forwarding
  • onPreview() delegates with progress forwarding
  • onDestroy() delegates with progress forwarding
  • onEnsureEnv() delegates to provider.EnsureEnv()
  • onParameters() delegates to provider.Parameters()
  • Progress messages sent back with matching request_id
  • Thread-safe lazy stream initialization
  • All handlers registered with broker.On()

Dependencies

Files

  • Create: pkg/azdext/provisioning_manager.go

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions