From e4e3d3a1f265210de3f8af8336ef116351124835 Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Thu, 2 Apr 2026 14:02:22 -0700 Subject: [PATCH 1/5] feat: Add provisioning provider support to extension framework Introduces extensibility for custom provisioning providers (e.g., Pulumi, CDK, scripts) via the existing gRPC extension framework, following the same MessageBroker-based patterns used by service targets and framework services. New components: - provisioning.proto: bidirectional streaming RPC with 9 request/response pairs for Initialize, State, Deploy, Preview, Destroy, EnsureEnv, and Parameters operations with progress streaming support - ProvisioningEnvelope: MessageEnvelope implementation for broker integration - ProvisioningManager: extension-side manager with factory-based provider creation and handler dispatch - ProvisioningService: server-side gRPC handler with capability verification and IoC container registration - ExternalProvisioningProvider: core-side adapter implementing provisioning.Provider that delegates to extensions via the broker - ExtensionHost.WithProvisioningProvider(): fluent API for extensions to register custom provisioning providers Wiring changes: - Added ProvisioningProviderCapability to extension registry - Added to listenCapabilities in extension middleware - Registered ProvisioningService in IoC container and gRPC server - Extended AzdClient with Provisioning() accessor - Relaxed ParseProvider() to accept any provider kind string - Added Options.Config field for extension-specific configuration Resolves #7465 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/azd/cmd/container.go | 1 + cli/azd/cmd/middleware/extensions.go | 1 + cli/azd/grpc/proto/provisioning.proto | 193 ++ .../external_provisioning_provider.go | 369 +++ .../grpcserver/project_service_test.go | 12 +- .../grpcserver/prompt_service_test.go | 1 + .../grpcserver/provisioning_service.go | 153 ++ cli/azd/internal/grpcserver/server.go | 4 + cli/azd/internal/grpcserver/server_test.go | 2 + cli/azd/pkg/azdext/azd_client.go | 10 + cli/azd/pkg/azdext/extension_host.go | 66 +- cli/azd/pkg/azdext/provisioning.pb.go | 2180 +++++++++++++++++ cli/azd/pkg/azdext/provisioning_envelope.go | 119 + cli/azd/pkg/azdext/provisioning_grpc.pb.go | 120 + cli/azd/pkg/azdext/provisioning_manager.go | 363 +++ cli/azd/pkg/extensions/registry.go | 2 + cli/azd/pkg/infra/provisioning/provider.go | 2 + .../pkg/infra/provisioning/provisioning.go | 9 +- .../infra/provisioning/provisioning_test.go | 36 +- 19 files changed, 3601 insertions(+), 42 deletions(-) create mode 100644 cli/azd/grpc/proto/provisioning.proto create mode 100644 cli/azd/internal/grpcserver/external_provisioning_provider.go create mode 100644 cli/azd/internal/grpcserver/provisioning_service.go create mode 100644 cli/azd/pkg/azdext/provisioning.pb.go create mode 100644 cli/azd/pkg/azdext/provisioning_envelope.go create mode 100644 cli/azd/pkg/azdext/provisioning_grpc.pb.go create mode 100644 cli/azd/pkg/azdext/provisioning_manager.go diff --git a/cli/azd/cmd/container.go b/cli/azd/cmd/container.go index 95cd937c48e..785d85166a5 100644 --- a/cli/azd/cmd/container.go +++ b/cli/azd/cmd/container.go @@ -941,6 +941,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) { container.MustRegisterSingleton(grpcserver.NewExtensionService) container.MustRegisterSingleton(grpcserver.NewServiceTargetService) container.MustRegisterSingleton(grpcserver.NewFrameworkService) + container.MustRegisterSingleton(grpcserver.NewProvisioningService) container.MustRegisterSingleton(grpcserver.NewAiModelService) container.MustRegisterScoped(grpcserver.NewCopilotService) diff --git a/cli/azd/cmd/middleware/extensions.go b/cli/azd/cmd/middleware/extensions.go index 2aac4b368b2..5dd7d354c5e 100644 --- a/cli/azd/cmd/middleware/extensions.go +++ b/cli/azd/cmd/middleware/extensions.go @@ -30,6 +30,7 @@ var ( extensions.LifecycleEventsCapability, extensions.ServiceTargetProviderCapability, extensions.FrameworkServiceProviderCapability, + extensions.ProvisioningProviderCapability, } ) diff --git a/cli/azd/grpc/proto/provisioning.proto b/cli/azd/grpc/proto/provisioning.proto new file mode 100644 index 00000000000..70bb27f938c --- /dev/null +++ b/cli/azd/grpc/proto/provisioning.proto @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +syntax = "proto3"; + +package azdext; + +option go_package = "github.com/azure/azure-dev/cli/azd/pkg/azdext"; + +import "include/google/protobuf/struct.proto"; +import "errors.proto"; + +service ProvisioningService { + // Bidirectional stream for provisioning requests and responses + rpc Stream(stream ProvisioningMessage) returns (stream ProvisioningMessage); +} + +// Envelope for all possible provisioning messages (requests and responses) +message ProvisioningMessage { + string request_id = 1; + ExtensionError error = 99; + oneof message_type { + RegisterProvisioningProviderRequest register_provisioning_provider_request = 2; + RegisterProvisioningProviderResponse register_provisioning_provider_response = 3; + ProvisioningInitializeRequest initialize_request = 4; + ProvisioningInitializeResponse initialize_response = 5; + ProvisioningStateRequest state_request = 6; + ProvisioningStateResponse state_response = 7; + ProvisioningDeployRequest deploy_request = 8; + ProvisioningDeployResponse deploy_response = 9; + ProvisioningPreviewRequest preview_request = 10; + ProvisioningPreviewResponse preview_response = 11; + ProvisioningDestroyRequest destroy_request = 12; + ProvisioningDestroyResponse destroy_response = 13; + ProvisioningEnsureEnvRequest ensure_env_request = 14; + ProvisioningEnsureEnvResponse ensure_env_response = 15; + ProvisioningParametersRequest parameters_request = 16; + ProvisioningParametersResponse parameters_response = 17; + ProvisioningProgressMessage progress_message = 18; + } +} + +// --- Registration --- + +message RegisterProvisioningProviderRequest { + string name = 1; +} + +message RegisterProvisioningProviderResponse {} + +// --- Initialize --- + +message ProvisioningInitializeRequest { + string project_path = 1; + ProvisioningOptions options = 2; +} + +message ProvisioningInitializeResponse {} + +// --- State --- + +message ProvisioningStateRequest { + ProvisioningStateOptions options = 1; +} + +message ProvisioningStateResponse { + ProvisioningStateResult state_result = 1; +} + +// --- Deploy --- + +message ProvisioningDeployRequest {} + +message ProvisioningDeployResponse { + ProvisioningDeployResult result = 1; +} + +// --- Preview --- + +message ProvisioningPreviewRequest {} + +message ProvisioningPreviewResponse { + ProvisioningPreviewResult result = 1; +} + +// --- Destroy --- + +message ProvisioningDestroyRequest { + ProvisioningDestroyOptions options = 1; +} + +message ProvisioningDestroyResponse { + ProvisioningDestroyResult result = 1; +} + +// --- EnsureEnv --- + +message ProvisioningEnsureEnvRequest {} + +message ProvisioningEnsureEnvResponse {} + +// --- Parameters --- + +message ProvisioningParametersRequest {} + +message ProvisioningParametersResponse { + repeated ProvisioningParameter parameters = 1; +} + +// --- Shared types --- + +message ProvisioningOptions { + string provider = 1; + string path = 2; + string module = 3; + map deployment_stacks = 4; + bool ignore_deployment_state = 5; + google.protobuf.Struct config = 6; +} + +message ProvisioningStateOptions {} + +message ProvisioningStateResult { + ProvisioningState state = 1; +} + +message ProvisioningState { + map outputs = 1; + repeated ProvisioningResource resources = 2; +} + +message ProvisioningDeployment { + map parameters = 1; + map outputs = 2; +} + +message ProvisioningDeployResult { + ProvisioningDeployment deployment = 1; + ProvisioningSkippedReason skipped_reason = 2; +} + +enum ProvisioningSkippedReason { + PROVISIONING_SKIPPED_REASON_UNSPECIFIED = 0; + PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE = 1; +} + +message ProvisioningDeploymentPreview { + string summary = 1; + map parameters = 2; + map outputs = 3; +} + +message ProvisioningPreviewResult { + ProvisioningDeploymentPreview preview = 1; +} + +message ProvisioningDestroyOptions { + bool force = 1; + bool purge = 2; +} + +message ProvisioningDestroyResult { + repeated string invalidated_env_keys = 1; +} + +message ProvisioningInputParameter { + string type = 1; + string default_value = 2; + string value = 3; +} + +message ProvisioningOutputParameter { + string type = 1; + string value = 2; +} + +message ProvisioningResource { + string id = 1; +} + +message ProvisioningParameter { + string name = 1; + bool secret = 2; + string value = 3; + repeated string env_var_mapping = 4; + bool local_prompt = 5; + bool using_env_var_mapping = 6; +} + +message ProvisioningProgressMessage { + string request_id = 1; + string message = 2; + int64 timestamp = 3; +} diff --git a/cli/azd/internal/grpcserver/external_provisioning_provider.go b/cli/azd/internal/grpcserver/external_provisioning_provider.go new file mode 100644 index 00000000000..6c37cbeb768 --- /dev/null +++ b/cli/azd/internal/grpcserver/external_provisioning_provider.go @@ -0,0 +1,369 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package grpcserver + +import ( + "context" + "fmt" + "log" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/environment" + "github.com/azure/azure-dev/cli/azd/pkg/extensions" + "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" + "github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning" + "github.com/azure/azure-dev/cli/azd/pkg/lazy" + "github.com/google/uuid" + "google.golang.org/protobuf/types/known/structpb" +) + +// ExternalProvisioningProvider implements provisioning.Provider by delegating to an extension. +type ExternalProvisioningProvider struct { + providerName string + extension *extensions.Extension + broker *grpcbroker.MessageBroker[azdext.ProvisioningMessage] + envManager environment.Manager + env *environment.Environment +} + +// NewExternalProvisioningProviderFactory returns a DI-compatible factory function. +func NewExternalProvisioningProviderFactory( + providerName string, + extension *extensions.Extension, + broker *grpcbroker.MessageBroker[azdext.ProvisioningMessage], + lazyEnv *lazy.Lazy[*environment.Environment], +) func(envManager environment.Manager) provisioning.Provider { + return func(envManager environment.Manager) provisioning.Provider { + env, _ := lazyEnv.GetValue() + return &ExternalProvisioningProvider{ + providerName: providerName, + extension: extension, + broker: broker, + envManager: envManager, + env: env, + } + } +} + +// Name returns the provisioning provider name. +func (p *ExternalProvisioningProvider) Name() string { + return p.providerName +} + +// Initialize initializes the provider with project path and options. +func (p *ExternalProvisioningProvider) Initialize( + ctx context.Context, + projectPath string, + options provisioning.Options, +) error { + protoOptions := convertToProtoOptions(options) + + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_InitializeRequest{ + InitializeRequest: &azdext.ProvisioningInitializeRequest{ + ProjectPath: projectPath, + Options: protoOptions, + }, + }, + } + + _, err := p.broker.SendAndWait(ctx, req) + return err +} + +// State returns the current state of provisioned infrastructure. +func (p *ExternalProvisioningProvider) State( + ctx context.Context, + options *provisioning.StateOptions, +) (*provisioning.StateResult, error) { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_StateRequest{ + StateRequest: &azdext.ProvisioningStateRequest{ + Options: &azdext.ProvisioningStateOptions{}, + }, + }, + } + + resp, err := p.broker.SendAndWait(ctx, req) + if err != nil { + return nil, err + } + + stateResp := resp.GetStateResponse() + if stateResp == nil || stateResp.StateResult == nil { + return &provisioning.StateResult{}, nil + } + + return convertFromProtoStateResult(stateResp.StateResult), nil +} + +// Deploy performs the provisioning deployment. +func (p *ExternalProvisioningProvider) Deploy( + ctx context.Context, +) (*provisioning.DeployResult, error) { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_DeployRequest{ + DeployRequest: &azdext.ProvisioningDeployRequest{}, + }, + } + + resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { + log.Printf("provisioning progress: %s", msg) + }) + if err != nil { + return nil, err + } + + deployResp := resp.GetDeployResponse() + if deployResp == nil || deployResp.Result == nil { + return &provisioning.DeployResult{}, nil + } + + return convertFromProtoDeployResult(deployResp.Result), nil +} + +// Preview returns a preview of what a deployment would change. +func (p *ExternalProvisioningProvider) Preview( + ctx context.Context, +) (*provisioning.DeployPreviewResult, error) { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_PreviewRequest{ + PreviewRequest: &azdext.ProvisioningPreviewRequest{}, + }, + } + + resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { + log.Printf("provisioning preview progress: %s", msg) + }) + if err != nil { + return nil, err + } + + previewResp := resp.GetPreviewResponse() + if previewResp == nil || previewResp.Result == nil { + return &provisioning.DeployPreviewResult{}, nil + } + + return convertFromProtoPreviewResult(previewResp.Result), nil +} + +// Destroy destroys the provisioned infrastructure. +func (p *ExternalProvisioningProvider) Destroy( + ctx context.Context, + options provisioning.DestroyOptions, +) (*provisioning.DestroyResult, error) { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_DestroyRequest{ + DestroyRequest: &azdext.ProvisioningDestroyRequest{ + Options: &azdext.ProvisioningDestroyOptions{ + Force: options.Force(), + Purge: options.Purge(), + }, + }, + }, + } + + resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { + log.Printf("provisioning destroy progress: %s", msg) + }) + if err != nil { + return nil, err + } + + destroyResp := resp.GetDestroyResponse() + if destroyResp == nil || destroyResp.Result == nil { + return &provisioning.DestroyResult{}, nil + } + + return &provisioning.DestroyResult{ + InvalidatedEnvKeys: destroyResp.Result.InvalidatedEnvKeys, + }, nil +} + +// EnsureEnv ensures the environment is configured properly. +func (p *ExternalProvisioningProvider) EnsureEnv(ctx context.Context) error { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_EnsureEnvRequest{ + EnsureEnvRequest: &azdext.ProvisioningEnsureEnvRequest{}, + }, + } + + _, err := p.broker.SendAndWait(ctx, req) + return err +} + +// Parameters returns the provisioning parameters. +func (p *ExternalProvisioningProvider) Parameters( + ctx context.Context, +) ([]provisioning.Parameter, error) { + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_ParametersRequest{ + ParametersRequest: &azdext.ProvisioningParametersRequest{}, + }, + } + + resp, err := p.broker.SendAndWait(ctx, req) + if err != nil { + return nil, err + } + + paramsResp := resp.GetParametersResponse() + if paramsResp == nil { + return nil, nil + } + + return convertFromProtoParameters(paramsResp.Parameters), nil +} + +// PlannedOutputs returns planned outputs. Not yet supported for external providers. +func (p *ExternalProvisioningProvider) PlannedOutputs( + ctx context.Context, +) ([]provisioning.PlannedOutput, error) { + return nil, nil +} + +// --- Conversion helpers --- + +func convertToProtoOptions( + options provisioning.Options, +) *azdext.ProvisioningOptions { + deploymentStacks := make(map[string]string, len(options.DeploymentStacks)) + for k, v := range options.DeploymentStacks { + deploymentStacks[k] = fmt.Sprintf("%v", v) + } + + protoOptions := &azdext.ProvisioningOptions{ + Provider: string(options.Provider), + Path: options.Path, + Module: options.Module, + DeploymentStacks: deploymentStacks, + IgnoreDeploymentState: options.IgnoreDeploymentState, + } + + // Convert Config to protobuf Struct if present + if options.Config != nil { + if s, err := structpb.NewStruct(options.Config); err == nil { + protoOptions.Config = s + } + } + + return protoOptions +} + +func convertFromProtoStateResult( + result *azdext.ProvisioningStateResult, +) *provisioning.StateResult { + if result == nil || result.State == nil { + return &provisioning.StateResult{} + } + + state := &provisioning.State{ + Outputs: make(map[string]provisioning.OutputParameter, len(result.State.Outputs)), + Resources: make([]provisioning.Resource, 0, len(result.State.Resources)), + } + + for k, v := range result.State.Outputs { + state.Outputs[k] = provisioning.OutputParameter{ + Type: provisioning.ParameterType(v.Type), + Value: v.Value, + } + } + + for _, r := range result.State.Resources { + state.Resources = append(state.Resources, provisioning.Resource{Id: r.Id}) + } + + return &provisioning.StateResult{State: state} +} + +func convertFromProtoDeployResult( + result *azdext.ProvisioningDeployResult, +) *provisioning.DeployResult { + deployResult := &provisioning.DeployResult{} + + if result.Deployment != nil { + deployment := &provisioning.Deployment{ + Parameters: make( + map[string]provisioning.InputParameter, + len(result.Deployment.Parameters), + ), + Outputs: make( + map[string]provisioning.OutputParameter, + len(result.Deployment.Outputs), + ), + } + + for k, v := range result.Deployment.Parameters { + deployment.Parameters[k] = provisioning.InputParameter{ + Type: v.Type, + Value: v.Value, + } + if v.DefaultValue != "" { + deployment.Parameters[k] = provisioning.InputParameter{ + Type: v.Type, + DefaultValue: v.DefaultValue, + Value: v.Value, + } + } + } + + for k, v := range result.Deployment.Outputs { + deployment.Outputs[k] = provisioning.OutputParameter{ + Type: provisioning.ParameterType(v.Type), + Value: v.Value, + } + } + + deployResult.Deployment = deployment + } + + //nolint:exhaustive + switch result.SkippedReason { + case azdext.ProvisioningSkippedReason_PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE: + deployResult.SkippedReason = provisioning.DeploymentStateSkipped + } + + return deployResult +} + +func convertFromProtoPreviewResult( + result *azdext.ProvisioningPreviewResult, +) *provisioning.DeployPreviewResult { + if result == nil || result.Preview == nil { + return &provisioning.DeployPreviewResult{} + } + + preview := &provisioning.DeploymentPreview{ + Status: result.Preview.Summary, + Properties: &provisioning.DeploymentPreviewProperties{ + Changes: []*provisioning.DeploymentPreviewChange{}, + }, + } + + return &provisioning.DeployPreviewResult{Preview: preview} +} + +func convertFromProtoParameters( + params []*azdext.ProvisioningParameter, +) []provisioning.Parameter { + result := make([]provisioning.Parameter, 0, len(params)) + for _, p := range params { + result = append(result, provisioning.Parameter{ + Name: p.Name, + Secret: p.Secret, + Value: p.Value, + EnvVarMapping: p.EnvVarMapping, + LocalPrompt: p.LocalPrompt, + UsingEnvVarMapping: p.UsingEnvVarMapping, + }) + } + return result +} diff --git a/cli/azd/internal/grpcserver/project_service_test.go b/cli/azd/internal/grpcserver/project_service_test.go index 66109b0ecca..ee3f44b00fe 100644 --- a/cli/azd/internal/grpcserver/project_service_test.go +++ b/cli/azd/internal/grpcserver/project_service_test.go @@ -15,6 +15,7 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext" "github.com/azure/azure-dev/cli/azd/pkg/exec" + "github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning" "github.com/azure/azure-dev/cli/azd/pkg/lazy" "github.com/azure/azure-dev/cli/azd/pkg/project" "github.com/azure/azure-dev/cli/azd/pkg/tools/github" @@ -1189,15 +1190,14 @@ func Test_ProjectService_TypeValidation_InvalidChangesNotPersisted(t *testing.T) Value: invalidProvider, }) - // SetConfigValue calls reloadAndCacheProjectConfig which calls project.Load - // project.Load fails because "999" is not a valid provider - require.Error(t, err) - require.Contains(t, err.Error(), "unsupported IaC provider '999'") + // ParseProvider now accepts any value to support extension-provided providers. + // Setting an int value is coerced to string "999", which is accepted. + require.NoError(t, err) - // Verify the change was NOT persisted to disk (should still be valid) + // Verify the value was persisted to disk reloadedConfig, err := project.Load(*mockContext.Context, azdContext.ProjectPath()) require.NoError(t, err) - require.Empty(t, reloadedConfig.Infra.Provider) + require.Equal(t, provisioning.ProviderKind("999"), reloadedConfig.Infra.Provider) }) t.Run("Service_SetHostToInt_CoercesToString", func(t *testing.T) { diff --git a/cli/azd/internal/grpcserver/prompt_service_test.go b/cli/azd/internal/grpcserver/prompt_service_test.go index e030bb3d826..165922c0307 100644 --- a/cli/azd/internal/grpcserver/prompt_service_test.go +++ b/cli/azd/internal/grpcserver/prompt_service_test.go @@ -601,6 +601,7 @@ func setupTestServer(t *testing.T, promptSvc azdext.PromptServiceServer) ( azdext.UnimplementedAccountServiceServer{}, azdext.UnimplementedAiModelServiceServer{}, azdext.UnimplementedCopilotServiceServer{}, + azdext.UnimplementedProvisioningServiceServer{}, ) serverInfo, err := server.Start() diff --git a/cli/azd/internal/grpcserver/provisioning_service.go b/cli/azd/internal/grpcserver/provisioning_service.go new file mode 100644 index 00000000000..590d2c5b98a --- /dev/null +++ b/cli/azd/internal/grpcserver/provisioning_service.go @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package grpcserver + +import ( + "context" + "errors" + "fmt" + "log" + "sync" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/environment" + "github.com/azure/azure-dev/cli/azd/pkg/extensions" + "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" + "github.com/azure/azure-dev/cli/azd/pkg/ioc" + "github.com/azure/azure-dev/cli/azd/pkg/lazy" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ProvisioningService implements azdext.ProvisioningServiceServer. +type ProvisioningService struct { + azdext.UnimplementedProvisioningServiceServer + container *ioc.NestedContainer + extensionManager *extensions.Manager + lazyEnv *lazy.Lazy[*environment.Environment] + providerMap map[string]*grpcbroker.MessageBroker[azdext.ProvisioningMessage] + providerMapMu sync.Mutex +} + +// NewProvisioningService creates a new ProvisioningService instance. +func NewProvisioningService( + container *ioc.NestedContainer, + extensionManager *extensions.Manager, + lazyEnv *lazy.Lazy[*environment.Environment], +) azdext.ProvisioningServiceServer { + return &ProvisioningService{ + container: container, + extensionManager: extensionManager, + lazyEnv: lazyEnv, + providerMap: make(map[string]*grpcbroker.MessageBroker[azdext.ProvisioningMessage]), + } +} + +// Stream handles the bi-directional streaming for provisioning operations. +func (s *ProvisioningService) Stream( + stream azdext.ProvisioningService_StreamServer, +) error { + ctx := stream.Context() + extensionClaims, err := extensions.GetClaimsFromContext(ctx) + if err != nil { + return fmt.Errorf("failed to get extension claims: %w", err) + } + + options := extensions.FilterOptions{ + Id: extensionClaims.Subject, + } + + extension, err := s.extensionManager.GetInstalled(options) + if err != nil { + return status.Errorf( + codes.FailedPrecondition, "failed to get extension: %s", err.Error(), + ) + } + + if !extension.HasCapability(extensions.ProvisioningProviderCapability) { + return status.Errorf( + codes.PermissionDenied, + "extension does not support provisioning-provider capability", + ) + } + + // Create message broker for this stream + ops := azdext.NewProvisioningEnvelope() + broker := grpcbroker.NewMessageBroker(stream, ops, extension.Id, log.Default()) + + // Track the provider name for cleanup when stream closes + var registeredProviderName string + + // Register handler for RegisterProvisioningProviderRequest + err = broker.On(func( + ctx context.Context, + req *azdext.RegisterProvisioningProviderRequest, + ) (*azdext.ProvisioningMessage, error) { + return s.onRegisterRequest( + ctx, req, extension, broker, ®isteredProviderName, + ) + }) + + if err != nil { + return fmt.Errorf("failed to register handler: %w", err) + } + + // Run the broker dispatcher (blocking) + if err := broker.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { + log.Printf("Broker error for provider %s: %v", registeredProviderName, err) + return fmt.Errorf("broker error: %w", err) + } + + s.providerMapMu.Lock() + delete(s.providerMap, registeredProviderName) + s.providerMapMu.Unlock() + + return nil +} + +// onRegisterRequest handles the registration of a provisioning provider +func (s *ProvisioningService) onRegisterRequest( + ctx context.Context, + req *azdext.RegisterProvisioningProviderRequest, + extension *extensions.Extension, + broker *grpcbroker.MessageBroker[azdext.ProvisioningMessage], + registeredProviderName *string, +) (*azdext.ProvisioningMessage, error) { + providerName := req.GetName() + s.providerMapMu.Lock() + defer s.providerMapMu.Unlock() + + if _, has := s.providerMap[providerName]; has { + return nil, status.Errorf( + codes.AlreadyExists, "provider %s already registered", providerName, + ) + } + + // Register external provisioning provider with DI container + err := s.container.RegisterNamedTransient( + providerName, + NewExternalProvisioningProviderFactory( + providerName, extension, broker, s.lazyEnv, + ), + ) + + if err != nil { + return nil, status.Errorf( + codes.Internal, + "failed to register provisioning provider: %s", + err.Error(), + ) + } + + s.providerMap[providerName] = broker + *registeredProviderName = providerName + log.Printf("Registered provisioning provider: %s", providerName) + + // Return response envelope + return &azdext.ProvisioningMessage{ + MessageType: &azdext.ProvisioningMessage_RegisterProvisioningProviderResponse{ + RegisterProvisioningProviderResponse: &azdext.RegisterProvisioningProviderResponse{}, + }, + }, nil +} diff --git a/cli/azd/internal/grpcserver/server.go b/cli/azd/internal/grpcserver/server.go index c19ca10e45c..d83619cb8eb 100644 --- a/cli/azd/internal/grpcserver/server.go +++ b/cli/azd/internal/grpcserver/server.go @@ -44,6 +44,7 @@ type Server struct { accountService azdext.AccountServiceServer aiModelService azdext.AiModelServiceServer copilotService azdext.CopilotServiceServer + provisioningService azdext.ProvisioningServiceServer } func NewServer( @@ -62,6 +63,7 @@ func NewServer( accountService azdext.AccountServiceServer, aiModelService azdext.AiModelServiceServer, copilotService azdext.CopilotServiceServer, + provisioningService azdext.ProvisioningServiceServer, ) *Server { return &Server{ projectService: projectService, @@ -79,6 +81,7 @@ func NewServer( accountService: accountService, aiModelService: aiModelService, copilotService: copilotService, + provisioningService: provisioningService, } } @@ -126,6 +129,7 @@ func (s *Server) Start() (*ServerInfo, error) { azdext.RegisterAccountServiceServer(s.grpcServer, s.accountService) azdext.RegisterAiModelServiceServer(s.grpcServer, s.aiModelService) azdext.RegisterCopilotServiceServer(s.grpcServer, s.copilotService) + azdext.RegisterProvisioningServiceServer(s.grpcServer, s.provisioningService) serverInfo.Address = fmt.Sprintf("127.0.0.1:%d", randomPort) serverInfo.Port = randomPort diff --git a/cli/azd/internal/grpcserver/server_test.go b/cli/azd/internal/grpcserver/server_test.go index 8416d3a603b..9bffcdb29ab 100644 --- a/cli/azd/internal/grpcserver/server_test.go +++ b/cli/azd/internal/grpcserver/server_test.go @@ -41,6 +41,7 @@ func Test_Server_Start(t *testing.T) { azdext.UnimplementedAccountServiceServer{}, azdext.UnimplementedAiModelServiceServer{}, azdext.UnimplementedCopilotServiceServer{}, + azdext.UnimplementedProvisioningServiceServer{}, ) serverInfo, err := server.Start() @@ -128,6 +129,7 @@ func Test_Server_StreamInterceptor(t *testing.T) { azdext.UnimplementedAccountServiceServer{}, azdext.UnimplementedAiModelServiceServer{}, azdext.UnimplementedCopilotServiceServer{}, + azdext.UnimplementedProvisioningServiceServer{}, ) serverInfo, err := server.Start() diff --git a/cli/azd/pkg/azdext/azd_client.go b/cli/azd/pkg/azdext/azd_client.go index 12425799322..150e57921ee 100644 --- a/cli/azd/pkg/azdext/azd_client.go +++ b/cli/azd/pkg/azdext/azd_client.go @@ -34,6 +34,7 @@ type AzdClient struct { accountClient AccountServiceClient aiClient AiModelServiceClient copilotClient CopilotServiceClient + provisioningClient ProvisioningServiceClient } // WithAddress sets the address of the `azd` gRPC server. @@ -241,3 +242,12 @@ func (c *AzdClient) Copilot() CopilotServiceClient { return c.copilotClient } + +// Provisioning returns the provisioning service client. +func (c *AzdClient) Provisioning() ProvisioningServiceClient { + if c.provisioningClient == nil { + c.provisioningClient = NewProvisioningServiceClient(c.connection) + } + + return c.provisioningClient +} diff --git a/cli/azd/pkg/azdext/extension_host.go b/cli/azd/pkg/azdext/extension_host.go index 0980c9f8a6f..b4bf3d2b6c5 100644 --- a/cli/azd/pkg/azdext/extension_host.go +++ b/cli/azd/pkg/azdext/extension_host.go @@ -42,6 +42,13 @@ type extensionEventManager interface { Close() error } +type provisioningRegistrar interface { + Register(ctx context.Context, factory ProvisioningProviderFactory, providerName string) error + Receive(ctx context.Context) error + Ready(ctx context.Context) error + Close() error +} + // ServiceTargetRegistration describes a service target provider to register with azd core. type ServiceTargetRegistration struct { Host string @@ -67,6 +74,12 @@ type ServiceEventRegistration struct { Options *ServiceEventOptions } +// ProvisioningProviderRegistration describes a provisioning provider to register with azd core. +type ProvisioningProviderRegistration struct { + Name string + Factory ProvisioningProviderFactory +} + // ProviderFactory describes a function that creates a provider instance type ProviderFactory[T any] func() T @@ -80,14 +93,16 @@ type FrameworkServiceFactory ProviderFactory[FrameworkServiceProvider] type ExtensionHost struct { client *AzdClient - serviceTargets []ServiceTargetRegistration - frameworkServices []FrameworkServiceRegistration - projectHandlers []ProjectEventRegistration - serviceHandlers []ServiceEventRegistration + serviceTargets []ServiceTargetRegistration + frameworkServices []FrameworkServiceRegistration + projectHandlers []ProjectEventRegistration + serviceHandlers []ServiceEventRegistration + provisioningProviders []ProvisioningProviderRegistration serviceTargetManager serviceTargetRegistrar frameworkServiceManager frameworkServiceRegistrar eventManager extensionEventManager + provisioningManager provisioningRegistrar } // NewExtensionHost creates a new ExtensionHost for the supplied azd client. @@ -120,6 +135,9 @@ func (er *ExtensionHost) initManagers(extensionId string, brokerLogger *log.Logg if er.eventManager == nil { er.eventManager = NewEventManager(extensionId, er.client, brokerLogger) } + if er.provisioningManager == nil { + er.provisioningManager = NewProvisioningManager(extensionId, er.client, brokerLogger) + } } // WithServiceTarget registers a service target provider to be wired when Run is invoked. @@ -154,6 +172,18 @@ func (er *ExtensionHost) WithServiceEventHandler( return er } +// WithProvisioningProvider registers a provisioning provider to be wired when Run is invoked. +func (er *ExtensionHost) WithProvisioningProvider( + name string, + factory ProvisioningProviderFactory, +) *ExtensionHost { + er.provisioningProviders = append( + er.provisioningProviders, + ProvisioningProviderRegistration{Name: name, Factory: factory}, + ) + return er +} + // Run wires the configured service targets and event handlers, signals readiness, and blocks until shutdown. func (er *ExtensionHost) Run(ctx context.Context) error { extensionId := getExtensionId(ctx) @@ -182,6 +212,7 @@ func (er *ExtensionHost) Run(ctx context.Context) error { hasServiceTargets := len(er.serviceTargets) > 0 hasFrameworkServices := len(er.frameworkServices) > 0 hasEventHandlers := len(er.projectHandlers) > 0 || len(er.serviceHandlers) > 0 + hasProvisioningProviders := len(er.provisioningProviders) > 0 // Set up defer for cleanup defer func() { @@ -194,6 +225,9 @@ func (er *ExtensionHost) Run(ctx context.Context) error { if hasEventHandlers { _ = er.eventManager.Close() } + if hasProvisioningProviders { + _ = er.provisioningManager.Close() + } }() // Collect active receivers and start them BEFORE registration @@ -208,6 +242,9 @@ func (er *ExtensionHost) Run(ctx context.Context) error { if hasEventHandlers { receivers = append(receivers, er.eventManager) } + if hasProvisioningProviders { + receivers = append(receivers, er.provisioningManager) + } // Start receiving messages from active managers in separate goroutines // CRITICAL: This must happen BEFORE any Register() calls that use broker.Send() @@ -239,9 +276,10 @@ func (er *ExtensionHost) Run(ctx context.Context) error { // Now that receivers are running, perform registrations // The broker.Run() in each Receive() will process the registration responses - // Register all registrations in parallel - service targets, framework services, and event handlers + // Register all registrations in parallel - service targets, framework services, event handlers, and provisioning var registrationsWaitGroup sync.WaitGroup - totalCount := len(er.serviceTargets) + len(er.frameworkServices) + len(er.projectHandlers) + len(er.serviceHandlers) + totalCount := len(er.serviceTargets) + len(er.frameworkServices) + + len(er.projectHandlers) + len(er.serviceHandlers) + len(er.provisioningProviders) registrationErrChan := make(chan error, totalCount) // Register service targets in parallel @@ -300,6 +338,22 @@ func (er *ExtensionHost) Run(ctx context.Context) error { }) } + // Register provisioning providers in parallel + for _, reg := range er.provisioningProviders { + if reg.Factory == nil { + return fmt.Errorf("provisioning provider for '%s' is nil", reg.Name) + } + + r := reg + registrationsWaitGroup.Go(func() { + if err := er.provisioningManager.Register(ctx, r.Factory, r.Name); err != nil { + registrationErrChan <- fmt.Errorf( + "failed to register provisioning provider '%s': %w", r.Name, err, + ) + } + }) + } + // Wait for ALL registrations to complete in parallel registrationsWaitGroup.Wait() close(registrationErrChan) diff --git a/cli/azd/pkg/azdext/provisioning.pb.go b/cli/azd/pkg/azdext/provisioning.pb.go new file mode 100644 index 00000000000..cb7c96ef9b7 --- /dev/null +++ b/cli/azd/pkg/azdext/provisioning.pb.go @@ -0,0 +1,2180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.32.1 +// source: provisioning.proto + +package azdext + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ProvisioningSkippedReason int32 + +const ( + ProvisioningSkippedReason_PROVISIONING_SKIPPED_REASON_UNSPECIFIED ProvisioningSkippedReason = 0 + ProvisioningSkippedReason_PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE ProvisioningSkippedReason = 1 +) + +// Enum value maps for ProvisioningSkippedReason. +var ( + ProvisioningSkippedReason_name = map[int32]string{ + 0: "PROVISIONING_SKIPPED_REASON_UNSPECIFIED", + 1: "PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE", + } + ProvisioningSkippedReason_value = map[string]int32{ + "PROVISIONING_SKIPPED_REASON_UNSPECIFIED": 0, + "PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE": 1, + } +) + +func (x ProvisioningSkippedReason) Enum() *ProvisioningSkippedReason { + p := new(ProvisioningSkippedReason) + *p = x + return p +} + +func (x ProvisioningSkippedReason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ProvisioningSkippedReason) Descriptor() protoreflect.EnumDescriptor { + return file_provisioning_proto_enumTypes[0].Descriptor() +} + +func (ProvisioningSkippedReason) Type() protoreflect.EnumType { + return &file_provisioning_proto_enumTypes[0] +} + +func (x ProvisioningSkippedReason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ProvisioningSkippedReason.Descriptor instead. +func (ProvisioningSkippedReason) EnumDescriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{0} +} + +// Envelope for all possible provisioning messages (requests and responses) +type ProvisioningMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Error *ExtensionError `protobuf:"bytes,99,opt,name=error,proto3" json:"error,omitempty"` + // Types that are valid to be assigned to MessageType: + // + // *ProvisioningMessage_RegisterProvisioningProviderRequest + // *ProvisioningMessage_RegisterProvisioningProviderResponse + // *ProvisioningMessage_InitializeRequest + // *ProvisioningMessage_InitializeResponse + // *ProvisioningMessage_StateRequest + // *ProvisioningMessage_StateResponse + // *ProvisioningMessage_DeployRequest + // *ProvisioningMessage_DeployResponse + // *ProvisioningMessage_PreviewRequest + // *ProvisioningMessage_PreviewResponse + // *ProvisioningMessage_DestroyRequest + // *ProvisioningMessage_DestroyResponse + // *ProvisioningMessage_EnsureEnvRequest + // *ProvisioningMessage_EnsureEnvResponse + // *ProvisioningMessage_ParametersRequest + // *ProvisioningMessage_ParametersResponse + // *ProvisioningMessage_ProgressMessage + MessageType isProvisioningMessage_MessageType `protobuf_oneof:"message_type"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningMessage) Reset() { + *x = ProvisioningMessage{} + mi := &file_provisioning_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningMessage) ProtoMessage() {} + +func (x *ProvisioningMessage) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningMessage.ProtoReflect.Descriptor instead. +func (*ProvisioningMessage) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{0} +} + +func (x *ProvisioningMessage) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ProvisioningMessage) GetError() *ExtensionError { + if x != nil { + return x.Error + } + return nil +} + +func (x *ProvisioningMessage) GetMessageType() isProvisioningMessage_MessageType { + if x != nil { + return x.MessageType + } + return nil +} + +func (x *ProvisioningMessage) GetRegisterProvisioningProviderRequest() *RegisterProvisioningProviderRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_RegisterProvisioningProviderRequest); ok { + return x.RegisterProvisioningProviderRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetRegisterProvisioningProviderResponse() *RegisterProvisioningProviderResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_RegisterProvisioningProviderResponse); ok { + return x.RegisterProvisioningProviderResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetInitializeRequest() *ProvisioningInitializeRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_InitializeRequest); ok { + return x.InitializeRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetInitializeResponse() *ProvisioningInitializeResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_InitializeResponse); ok { + return x.InitializeResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetStateRequest() *ProvisioningStateRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_StateRequest); ok { + return x.StateRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetStateResponse() *ProvisioningStateResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_StateResponse); ok { + return x.StateResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetDeployRequest() *ProvisioningDeployRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_DeployRequest); ok { + return x.DeployRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetDeployResponse() *ProvisioningDeployResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_DeployResponse); ok { + return x.DeployResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetPreviewRequest() *ProvisioningPreviewRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_PreviewRequest); ok { + return x.PreviewRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetPreviewResponse() *ProvisioningPreviewResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_PreviewResponse); ok { + return x.PreviewResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetDestroyRequest() *ProvisioningDestroyRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_DestroyRequest); ok { + return x.DestroyRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetDestroyResponse() *ProvisioningDestroyResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_DestroyResponse); ok { + return x.DestroyResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetEnsureEnvRequest() *ProvisioningEnsureEnvRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_EnsureEnvRequest); ok { + return x.EnsureEnvRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetEnsureEnvResponse() *ProvisioningEnsureEnvResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_EnsureEnvResponse); ok { + return x.EnsureEnvResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetParametersRequest() *ProvisioningParametersRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_ParametersRequest); ok { + return x.ParametersRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetParametersResponse() *ProvisioningParametersResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_ParametersResponse); ok { + return x.ParametersResponse + } + } + return nil +} + +func (x *ProvisioningMessage) GetProgressMessage() *ProvisioningProgressMessage { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_ProgressMessage); ok { + return x.ProgressMessage + } + } + return nil +} + +type isProvisioningMessage_MessageType interface { + isProvisioningMessage_MessageType() +} + +type ProvisioningMessage_RegisterProvisioningProviderRequest struct { + RegisterProvisioningProviderRequest *RegisterProvisioningProviderRequest `protobuf:"bytes,2,opt,name=register_provisioning_provider_request,json=registerProvisioningProviderRequest,proto3,oneof"` +} + +type ProvisioningMessage_RegisterProvisioningProviderResponse struct { + RegisterProvisioningProviderResponse *RegisterProvisioningProviderResponse `protobuf:"bytes,3,opt,name=register_provisioning_provider_response,json=registerProvisioningProviderResponse,proto3,oneof"` +} + +type ProvisioningMessage_InitializeRequest struct { + InitializeRequest *ProvisioningInitializeRequest `protobuf:"bytes,4,opt,name=initialize_request,json=initializeRequest,proto3,oneof"` +} + +type ProvisioningMessage_InitializeResponse struct { + InitializeResponse *ProvisioningInitializeResponse `protobuf:"bytes,5,opt,name=initialize_response,json=initializeResponse,proto3,oneof"` +} + +type ProvisioningMessage_StateRequest struct { + StateRequest *ProvisioningStateRequest `protobuf:"bytes,6,opt,name=state_request,json=stateRequest,proto3,oneof"` +} + +type ProvisioningMessage_StateResponse struct { + StateResponse *ProvisioningStateResponse `protobuf:"bytes,7,opt,name=state_response,json=stateResponse,proto3,oneof"` +} + +type ProvisioningMessage_DeployRequest struct { + DeployRequest *ProvisioningDeployRequest `protobuf:"bytes,8,opt,name=deploy_request,json=deployRequest,proto3,oneof"` +} + +type ProvisioningMessage_DeployResponse struct { + DeployResponse *ProvisioningDeployResponse `protobuf:"bytes,9,opt,name=deploy_response,json=deployResponse,proto3,oneof"` +} + +type ProvisioningMessage_PreviewRequest struct { + PreviewRequest *ProvisioningPreviewRequest `protobuf:"bytes,10,opt,name=preview_request,json=previewRequest,proto3,oneof"` +} + +type ProvisioningMessage_PreviewResponse struct { + PreviewResponse *ProvisioningPreviewResponse `protobuf:"bytes,11,opt,name=preview_response,json=previewResponse,proto3,oneof"` +} + +type ProvisioningMessage_DestroyRequest struct { + DestroyRequest *ProvisioningDestroyRequest `protobuf:"bytes,12,opt,name=destroy_request,json=destroyRequest,proto3,oneof"` +} + +type ProvisioningMessage_DestroyResponse struct { + DestroyResponse *ProvisioningDestroyResponse `protobuf:"bytes,13,opt,name=destroy_response,json=destroyResponse,proto3,oneof"` +} + +type ProvisioningMessage_EnsureEnvRequest struct { + EnsureEnvRequest *ProvisioningEnsureEnvRequest `protobuf:"bytes,14,opt,name=ensure_env_request,json=ensureEnvRequest,proto3,oneof"` +} + +type ProvisioningMessage_EnsureEnvResponse struct { + EnsureEnvResponse *ProvisioningEnsureEnvResponse `protobuf:"bytes,15,opt,name=ensure_env_response,json=ensureEnvResponse,proto3,oneof"` +} + +type ProvisioningMessage_ParametersRequest struct { + ParametersRequest *ProvisioningParametersRequest `protobuf:"bytes,16,opt,name=parameters_request,json=parametersRequest,proto3,oneof"` +} + +type ProvisioningMessage_ParametersResponse struct { + ParametersResponse *ProvisioningParametersResponse `protobuf:"bytes,17,opt,name=parameters_response,json=parametersResponse,proto3,oneof"` +} + +type ProvisioningMessage_ProgressMessage struct { + ProgressMessage *ProvisioningProgressMessage `protobuf:"bytes,18,opt,name=progress_message,json=progressMessage,proto3,oneof"` +} + +func (*ProvisioningMessage_RegisterProvisioningProviderRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_RegisterProvisioningProviderResponse) isProvisioningMessage_MessageType() { +} + +func (*ProvisioningMessage_InitializeRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_InitializeResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_StateRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_StateResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_DeployRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_DeployResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_PreviewRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_PreviewResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_DestroyRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_DestroyResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_EnsureEnvRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_EnsureEnvResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_ParametersRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_ParametersResponse) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_ProgressMessage) isProvisioningMessage_MessageType() {} + +type RegisterProvisioningProviderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterProvisioningProviderRequest) Reset() { + *x = RegisterProvisioningProviderRequest{} + mi := &file_provisioning_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterProvisioningProviderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProvisioningProviderRequest) ProtoMessage() {} + +func (x *RegisterProvisioningProviderRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProvisioningProviderRequest.ProtoReflect.Descriptor instead. +func (*RegisterProvisioningProviderRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterProvisioningProviderRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type RegisterProvisioningProviderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterProvisioningProviderResponse) Reset() { + *x = RegisterProvisioningProviderResponse{} + mi := &file_provisioning_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterProvisioningProviderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterProvisioningProviderResponse) ProtoMessage() {} + +func (x *RegisterProvisioningProviderResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterProvisioningProviderResponse.ProtoReflect.Descriptor instead. +func (*RegisterProvisioningProviderResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{2} +} + +type ProvisioningInitializeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProjectPath string `protobuf:"bytes,1,opt,name=project_path,json=projectPath,proto3" json:"project_path,omitempty"` + Options *ProvisioningOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningInitializeRequest) Reset() { + *x = ProvisioningInitializeRequest{} + mi := &file_provisioning_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningInitializeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningInitializeRequest) ProtoMessage() {} + +func (x *ProvisioningInitializeRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningInitializeRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningInitializeRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{3} +} + +func (x *ProvisioningInitializeRequest) GetProjectPath() string { + if x != nil { + return x.ProjectPath + } + return "" +} + +func (x *ProvisioningInitializeRequest) GetOptions() *ProvisioningOptions { + if x != nil { + return x.Options + } + return nil +} + +type ProvisioningInitializeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningInitializeResponse) Reset() { + *x = ProvisioningInitializeResponse{} + mi := &file_provisioning_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningInitializeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningInitializeResponse) ProtoMessage() {} + +func (x *ProvisioningInitializeResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningInitializeResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningInitializeResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{4} +} + +type ProvisioningStateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Options *ProvisioningStateOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningStateRequest) Reset() { + *x = ProvisioningStateRequest{} + mi := &file_provisioning_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningStateRequest) ProtoMessage() {} + +func (x *ProvisioningStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningStateRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningStateRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{5} +} + +func (x *ProvisioningStateRequest) GetOptions() *ProvisioningStateOptions { + if x != nil { + return x.Options + } + return nil +} + +type ProvisioningStateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + StateResult *ProvisioningStateResult `protobuf:"bytes,1,opt,name=state_result,json=stateResult,proto3" json:"state_result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningStateResponse) Reset() { + *x = ProvisioningStateResponse{} + mi := &file_provisioning_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningStateResponse) ProtoMessage() {} + +func (x *ProvisioningStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningStateResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningStateResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{6} +} + +func (x *ProvisioningStateResponse) GetStateResult() *ProvisioningStateResult { + if x != nil { + return x.StateResult + } + return nil +} + +type ProvisioningDeployRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDeployRequest) Reset() { + *x = ProvisioningDeployRequest{} + mi := &file_provisioning_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDeployRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDeployRequest) ProtoMessage() {} + +func (x *ProvisioningDeployRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDeployRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningDeployRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{7} +} + +type ProvisioningDeployResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Result *ProvisioningDeployResult `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDeployResponse) Reset() { + *x = ProvisioningDeployResponse{} + mi := &file_provisioning_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDeployResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDeployResponse) ProtoMessage() {} + +func (x *ProvisioningDeployResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDeployResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningDeployResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{8} +} + +func (x *ProvisioningDeployResponse) GetResult() *ProvisioningDeployResult { + if x != nil { + return x.Result + } + return nil +} + +type ProvisioningPreviewRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPreviewRequest) Reset() { + *x = ProvisioningPreviewRequest{} + mi := &file_provisioning_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPreviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPreviewRequest) ProtoMessage() {} + +func (x *ProvisioningPreviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPreviewRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningPreviewRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{9} +} + +type ProvisioningPreviewResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Result *ProvisioningPreviewResult `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPreviewResponse) Reset() { + *x = ProvisioningPreviewResponse{} + mi := &file_provisioning_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPreviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPreviewResponse) ProtoMessage() {} + +func (x *ProvisioningPreviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPreviewResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningPreviewResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{10} +} + +func (x *ProvisioningPreviewResponse) GetResult() *ProvisioningPreviewResult { + if x != nil { + return x.Result + } + return nil +} + +type ProvisioningDestroyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Options *ProvisioningDestroyOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDestroyRequest) Reset() { + *x = ProvisioningDestroyRequest{} + mi := &file_provisioning_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDestroyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDestroyRequest) ProtoMessage() {} + +func (x *ProvisioningDestroyRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDestroyRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningDestroyRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{11} +} + +func (x *ProvisioningDestroyRequest) GetOptions() *ProvisioningDestroyOptions { + if x != nil { + return x.Options + } + return nil +} + +type ProvisioningDestroyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Result *ProvisioningDestroyResult `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDestroyResponse) Reset() { + *x = ProvisioningDestroyResponse{} + mi := &file_provisioning_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDestroyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDestroyResponse) ProtoMessage() {} + +func (x *ProvisioningDestroyResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDestroyResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningDestroyResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{12} +} + +func (x *ProvisioningDestroyResponse) GetResult() *ProvisioningDestroyResult { + if x != nil { + return x.Result + } + return nil +} + +type ProvisioningEnsureEnvRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningEnsureEnvRequest) Reset() { + *x = ProvisioningEnsureEnvRequest{} + mi := &file_provisioning_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningEnsureEnvRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningEnsureEnvRequest) ProtoMessage() {} + +func (x *ProvisioningEnsureEnvRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningEnsureEnvRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningEnsureEnvRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{13} +} + +type ProvisioningEnsureEnvResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningEnsureEnvResponse) Reset() { + *x = ProvisioningEnsureEnvResponse{} + mi := &file_provisioning_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningEnsureEnvResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningEnsureEnvResponse) ProtoMessage() {} + +func (x *ProvisioningEnsureEnvResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningEnsureEnvResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningEnsureEnvResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{14} +} + +type ProvisioningParametersRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningParametersRequest) Reset() { + *x = ProvisioningParametersRequest{} + mi := &file_provisioning_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningParametersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningParametersRequest) ProtoMessage() {} + +func (x *ProvisioningParametersRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningParametersRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningParametersRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{15} +} + +type ProvisioningParametersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parameters []*ProvisioningParameter `protobuf:"bytes,1,rep,name=parameters,proto3" json:"parameters,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningParametersResponse) Reset() { + *x = ProvisioningParametersResponse{} + mi := &file_provisioning_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningParametersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningParametersResponse) ProtoMessage() {} + +func (x *ProvisioningParametersResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningParametersResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningParametersResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{16} +} + +func (x *ProvisioningParametersResponse) GetParameters() []*ProvisioningParameter { + if x != nil { + return x.Parameters + } + return nil +} + +type ProvisioningOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Module string `protobuf:"bytes,3,opt,name=module,proto3" json:"module,omitempty"` + DeploymentStacks map[string]string `protobuf:"bytes,4,rep,name=deployment_stacks,json=deploymentStacks,proto3" json:"deployment_stacks,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + IgnoreDeploymentState bool `protobuf:"varint,5,opt,name=ignore_deployment_state,json=ignoreDeploymentState,proto3" json:"ignore_deployment_state,omitempty"` + Config *structpb.Struct `protobuf:"bytes,6,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningOptions) Reset() { + *x = ProvisioningOptions{} + mi := &file_provisioning_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningOptions) ProtoMessage() {} + +func (x *ProvisioningOptions) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningOptions.ProtoReflect.Descriptor instead. +func (*ProvisioningOptions) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{17} +} + +func (x *ProvisioningOptions) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *ProvisioningOptions) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ProvisioningOptions) GetModule() string { + if x != nil { + return x.Module + } + return "" +} + +func (x *ProvisioningOptions) GetDeploymentStacks() map[string]string { + if x != nil { + return x.DeploymentStacks + } + return nil +} + +func (x *ProvisioningOptions) GetIgnoreDeploymentState() bool { + if x != nil { + return x.IgnoreDeploymentState + } + return false +} + +func (x *ProvisioningOptions) GetConfig() *structpb.Struct { + if x != nil { + return x.Config + } + return nil +} + +type ProvisioningStateOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningStateOptions) Reset() { + *x = ProvisioningStateOptions{} + mi := &file_provisioning_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningStateOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningStateOptions) ProtoMessage() {} + +func (x *ProvisioningStateOptions) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningStateOptions.ProtoReflect.Descriptor instead. +func (*ProvisioningStateOptions) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{18} +} + +type ProvisioningStateResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + State *ProvisioningState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningStateResult) Reset() { + *x = ProvisioningStateResult{} + mi := &file_provisioning_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningStateResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningStateResult) ProtoMessage() {} + +func (x *ProvisioningStateResult) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningStateResult.ProtoReflect.Descriptor instead. +func (*ProvisioningStateResult) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{19} +} + +func (x *ProvisioningStateResult) GetState() *ProvisioningState { + if x != nil { + return x.State + } + return nil +} + +type ProvisioningState struct { + state protoimpl.MessageState `protogen:"open.v1"` + Outputs map[string]*ProvisioningOutputParameter `protobuf:"bytes,1,rep,name=outputs,proto3" json:"outputs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Resources []*ProvisioningResource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningState) Reset() { + *x = ProvisioningState{} + mi := &file_provisioning_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningState) ProtoMessage() {} + +func (x *ProvisioningState) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningState.ProtoReflect.Descriptor instead. +func (*ProvisioningState) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{20} +} + +func (x *ProvisioningState) GetOutputs() map[string]*ProvisioningOutputParameter { + if x != nil { + return x.Outputs + } + return nil +} + +func (x *ProvisioningState) GetResources() []*ProvisioningResource { + if x != nil { + return x.Resources + } + return nil +} + +type ProvisioningDeployment struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parameters map[string]*ProvisioningInputParameter `protobuf:"bytes,1,rep,name=parameters,proto3" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Outputs map[string]*ProvisioningOutputParameter `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDeployment) Reset() { + *x = ProvisioningDeployment{} + mi := &file_provisioning_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDeployment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDeployment) ProtoMessage() {} + +func (x *ProvisioningDeployment) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDeployment.ProtoReflect.Descriptor instead. +func (*ProvisioningDeployment) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{21} +} + +func (x *ProvisioningDeployment) GetParameters() map[string]*ProvisioningInputParameter { + if x != nil { + return x.Parameters + } + return nil +} + +func (x *ProvisioningDeployment) GetOutputs() map[string]*ProvisioningOutputParameter { + if x != nil { + return x.Outputs + } + return nil +} + +type ProvisioningDeployResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Deployment *ProvisioningDeployment `protobuf:"bytes,1,opt,name=deployment,proto3" json:"deployment,omitempty"` + SkippedReason ProvisioningSkippedReason `protobuf:"varint,2,opt,name=skipped_reason,json=skippedReason,proto3,enum=azdext.ProvisioningSkippedReason" json:"skipped_reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDeployResult) Reset() { + *x = ProvisioningDeployResult{} + mi := &file_provisioning_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDeployResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDeployResult) ProtoMessage() {} + +func (x *ProvisioningDeployResult) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDeployResult.ProtoReflect.Descriptor instead. +func (*ProvisioningDeployResult) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{22} +} + +func (x *ProvisioningDeployResult) GetDeployment() *ProvisioningDeployment { + if x != nil { + return x.Deployment + } + return nil +} + +func (x *ProvisioningDeployResult) GetSkippedReason() ProvisioningSkippedReason { + if x != nil { + return x.SkippedReason + } + return ProvisioningSkippedReason_PROVISIONING_SKIPPED_REASON_UNSPECIFIED +} + +type ProvisioningDeploymentPreview struct { + state protoimpl.MessageState `protogen:"open.v1"` + Summary string `protobuf:"bytes,1,opt,name=summary,proto3" json:"summary,omitempty"` + Parameters map[string]*ProvisioningInputParameter `protobuf:"bytes,2,rep,name=parameters,proto3" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Outputs map[string]*ProvisioningOutputParameter `protobuf:"bytes,3,rep,name=outputs,proto3" json:"outputs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDeploymentPreview) Reset() { + *x = ProvisioningDeploymentPreview{} + mi := &file_provisioning_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDeploymentPreview) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDeploymentPreview) ProtoMessage() {} + +func (x *ProvisioningDeploymentPreview) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDeploymentPreview.ProtoReflect.Descriptor instead. +func (*ProvisioningDeploymentPreview) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{23} +} + +func (x *ProvisioningDeploymentPreview) GetSummary() string { + if x != nil { + return x.Summary + } + return "" +} + +func (x *ProvisioningDeploymentPreview) GetParameters() map[string]*ProvisioningInputParameter { + if x != nil { + return x.Parameters + } + return nil +} + +func (x *ProvisioningDeploymentPreview) GetOutputs() map[string]*ProvisioningOutputParameter { + if x != nil { + return x.Outputs + } + return nil +} + +type ProvisioningPreviewResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Preview *ProvisioningDeploymentPreview `protobuf:"bytes,1,opt,name=preview,proto3" json:"preview,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPreviewResult) Reset() { + *x = ProvisioningPreviewResult{} + mi := &file_provisioning_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPreviewResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPreviewResult) ProtoMessage() {} + +func (x *ProvisioningPreviewResult) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPreviewResult.ProtoReflect.Descriptor instead. +func (*ProvisioningPreviewResult) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{24} +} + +func (x *ProvisioningPreviewResult) GetPreview() *ProvisioningDeploymentPreview { + if x != nil { + return x.Preview + } + return nil +} + +type ProvisioningDestroyOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` + Purge bool `protobuf:"varint,2,opt,name=purge,proto3" json:"purge,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDestroyOptions) Reset() { + *x = ProvisioningDestroyOptions{} + mi := &file_provisioning_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDestroyOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDestroyOptions) ProtoMessage() {} + +func (x *ProvisioningDestroyOptions) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDestroyOptions.ProtoReflect.Descriptor instead. +func (*ProvisioningDestroyOptions) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{25} +} + +func (x *ProvisioningDestroyOptions) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *ProvisioningDestroyOptions) GetPurge() bool { + if x != nil { + return x.Purge + } + return false +} + +type ProvisioningDestroyResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + InvalidatedEnvKeys []string `protobuf:"bytes,1,rep,name=invalidated_env_keys,json=invalidatedEnvKeys,proto3" json:"invalidated_env_keys,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningDestroyResult) Reset() { + *x = ProvisioningDestroyResult{} + mi := &file_provisioning_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningDestroyResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningDestroyResult) ProtoMessage() {} + +func (x *ProvisioningDestroyResult) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningDestroyResult.ProtoReflect.Descriptor instead. +func (*ProvisioningDestroyResult) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{26} +} + +func (x *ProvisioningDestroyResult) GetInvalidatedEnvKeys() []string { + if x != nil { + return x.InvalidatedEnvKeys + } + return nil +} + +type ProvisioningInputParameter struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + DefaultValue string `protobuf:"bytes,2,opt,name=default_value,json=defaultValue,proto3" json:"default_value,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningInputParameter) Reset() { + *x = ProvisioningInputParameter{} + mi := &file_provisioning_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningInputParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningInputParameter) ProtoMessage() {} + +func (x *ProvisioningInputParameter) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningInputParameter.ProtoReflect.Descriptor instead. +func (*ProvisioningInputParameter) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{27} +} + +func (x *ProvisioningInputParameter) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ProvisioningInputParameter) GetDefaultValue() string { + if x != nil { + return x.DefaultValue + } + return "" +} + +func (x *ProvisioningInputParameter) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ProvisioningOutputParameter struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningOutputParameter) Reset() { + *x = ProvisioningOutputParameter{} + mi := &file_provisioning_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningOutputParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningOutputParameter) ProtoMessage() {} + +func (x *ProvisioningOutputParameter) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningOutputParameter.ProtoReflect.Descriptor instead. +func (*ProvisioningOutputParameter) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{28} +} + +func (x *ProvisioningOutputParameter) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ProvisioningOutputParameter) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ProvisioningResource struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningResource) Reset() { + *x = ProvisioningResource{} + mi := &file_provisioning_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningResource) ProtoMessage() {} + +func (x *ProvisioningResource) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningResource.ProtoReflect.Descriptor instead. +func (*ProvisioningResource) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{29} +} + +func (x *ProvisioningResource) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ProvisioningParameter struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Secret bool `protobuf:"varint,2,opt,name=secret,proto3" json:"secret,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + EnvVarMapping []string `protobuf:"bytes,4,rep,name=env_var_mapping,json=envVarMapping,proto3" json:"env_var_mapping,omitempty"` + LocalPrompt bool `protobuf:"varint,5,opt,name=local_prompt,json=localPrompt,proto3" json:"local_prompt,omitempty"` + UsingEnvVarMapping bool `protobuf:"varint,6,opt,name=using_env_var_mapping,json=usingEnvVarMapping,proto3" json:"using_env_var_mapping,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningParameter) Reset() { + *x = ProvisioningParameter{} + mi := &file_provisioning_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningParameter) ProtoMessage() {} + +func (x *ProvisioningParameter) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningParameter.ProtoReflect.Descriptor instead. +func (*ProvisioningParameter) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{30} +} + +func (x *ProvisioningParameter) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProvisioningParameter) GetSecret() bool { + if x != nil { + return x.Secret + } + return false +} + +func (x *ProvisioningParameter) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *ProvisioningParameter) GetEnvVarMapping() []string { + if x != nil { + return x.EnvVarMapping + } + return nil +} + +func (x *ProvisioningParameter) GetLocalPrompt() bool { + if x != nil { + return x.LocalPrompt + } + return false +} + +func (x *ProvisioningParameter) GetUsingEnvVarMapping() bool { + if x != nil { + return x.UsingEnvVarMapping + } + return false +} + +type ProvisioningProgressMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningProgressMessage) Reset() { + *x = ProvisioningProgressMessage{} + mi := &file_provisioning_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningProgressMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningProgressMessage) ProtoMessage() {} + +func (x *ProvisioningProgressMessage) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[31] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningProgressMessage.ProtoReflect.Descriptor instead. +func (*ProvisioningProgressMessage) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{31} +} + +func (x *ProvisioningProgressMessage) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ProvisioningProgressMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *ProvisioningProgressMessage) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +var File_provisioning_proto protoreflect.FileDescriptor + +const file_provisioning_proto_rawDesc = "" + + "\n" + + "\x12provisioning.proto\x12\x06azdext\x1a$include/google/protobuf/struct.proto\x1a\ferrors.proto\"\xd8\f\n" + + "\x13ProvisioningMessage\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12,\n" + + "\x05error\x18c \x01(\v2\x16.azdext.ExtensionErrorR\x05error\x12\x82\x01\n" + + "®ister_provisioning_provider_request\x18\x02 \x01(\v2+.azdext.RegisterProvisioningProviderRequestH\x00R#registerProvisioningProviderRequest\x12\x85\x01\n" + + "'register_provisioning_provider_response\x18\x03 \x01(\v2,.azdext.RegisterProvisioningProviderResponseH\x00R$registerProvisioningProviderResponse\x12V\n" + + "\x12initialize_request\x18\x04 \x01(\v2%.azdext.ProvisioningInitializeRequestH\x00R\x11initializeRequest\x12Y\n" + + "\x13initialize_response\x18\x05 \x01(\v2&.azdext.ProvisioningInitializeResponseH\x00R\x12initializeResponse\x12G\n" + + "\rstate_request\x18\x06 \x01(\v2 .azdext.ProvisioningStateRequestH\x00R\fstateRequest\x12J\n" + + "\x0estate_response\x18\a \x01(\v2!.azdext.ProvisioningStateResponseH\x00R\rstateResponse\x12J\n" + + "\x0edeploy_request\x18\b \x01(\v2!.azdext.ProvisioningDeployRequestH\x00R\rdeployRequest\x12M\n" + + "\x0fdeploy_response\x18\t \x01(\v2\".azdext.ProvisioningDeployResponseH\x00R\x0edeployResponse\x12M\n" + + "\x0fpreview_request\x18\n" + + " \x01(\v2\".azdext.ProvisioningPreviewRequestH\x00R\x0epreviewRequest\x12P\n" + + "\x10preview_response\x18\v \x01(\v2#.azdext.ProvisioningPreviewResponseH\x00R\x0fpreviewResponse\x12M\n" + + "\x0fdestroy_request\x18\f \x01(\v2\".azdext.ProvisioningDestroyRequestH\x00R\x0edestroyRequest\x12P\n" + + "\x10destroy_response\x18\r \x01(\v2#.azdext.ProvisioningDestroyResponseH\x00R\x0fdestroyResponse\x12T\n" + + "\x12ensure_env_request\x18\x0e \x01(\v2$.azdext.ProvisioningEnsureEnvRequestH\x00R\x10ensureEnvRequest\x12W\n" + + "\x13ensure_env_response\x18\x0f \x01(\v2%.azdext.ProvisioningEnsureEnvResponseH\x00R\x11ensureEnvResponse\x12V\n" + + "\x12parameters_request\x18\x10 \x01(\v2%.azdext.ProvisioningParametersRequestH\x00R\x11parametersRequest\x12Y\n" + + "\x13parameters_response\x18\x11 \x01(\v2&.azdext.ProvisioningParametersResponseH\x00R\x12parametersResponse\x12P\n" + + "\x10progress_message\x18\x12 \x01(\v2#.azdext.ProvisioningProgressMessageH\x00R\x0fprogressMessageB\x0e\n" + + "\fmessage_type\"9\n" + + "#RegisterProvisioningProviderRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\"&\n" + + "$RegisterProvisioningProviderResponse\"y\n" + + "\x1dProvisioningInitializeRequest\x12!\n" + + "\fproject_path\x18\x01 \x01(\tR\vprojectPath\x125\n" + + "\aoptions\x18\x02 \x01(\v2\x1b.azdext.ProvisioningOptionsR\aoptions\" \n" + + "\x1eProvisioningInitializeResponse\"V\n" + + "\x18ProvisioningStateRequest\x12:\n" + + "\aoptions\x18\x01 \x01(\v2 .azdext.ProvisioningStateOptionsR\aoptions\"_\n" + + "\x19ProvisioningStateResponse\x12B\n" + + "\fstate_result\x18\x01 \x01(\v2\x1f.azdext.ProvisioningStateResultR\vstateResult\"\x1b\n" + + "\x19ProvisioningDeployRequest\"V\n" + + "\x1aProvisioningDeployResponse\x128\n" + + "\x06result\x18\x01 \x01(\v2 .azdext.ProvisioningDeployResultR\x06result\"\x1c\n" + + "\x1aProvisioningPreviewRequest\"X\n" + + "\x1bProvisioningPreviewResponse\x129\n" + + "\x06result\x18\x01 \x01(\v2!.azdext.ProvisioningPreviewResultR\x06result\"Z\n" + + "\x1aProvisioningDestroyRequest\x12<\n" + + "\aoptions\x18\x01 \x01(\v2\".azdext.ProvisioningDestroyOptionsR\aoptions\"X\n" + + "\x1bProvisioningDestroyResponse\x129\n" + + "\x06result\x18\x01 \x01(\v2!.azdext.ProvisioningDestroyResultR\x06result\"\x1e\n" + + "\x1cProvisioningEnsureEnvRequest\"\x1f\n" + + "\x1dProvisioningEnsureEnvResponse\"\x1f\n" + + "\x1dProvisioningParametersRequest\"_\n" + + "\x1eProvisioningParametersResponse\x12=\n" + + "\n" + + "parameters\x18\x01 \x03(\v2\x1d.azdext.ProvisioningParameterR\n" + + "parameters\"\xeb\x02\n" + + "\x13ProvisioningOptions\x12\x1a\n" + + "\bprovider\x18\x01 \x01(\tR\bprovider\x12\x12\n" + + "\x04path\x18\x02 \x01(\tR\x04path\x12\x16\n" + + "\x06module\x18\x03 \x01(\tR\x06module\x12^\n" + + "\x11deployment_stacks\x18\x04 \x03(\v21.azdext.ProvisioningOptions.DeploymentStacksEntryR\x10deploymentStacks\x126\n" + + "\x17ignore_deployment_state\x18\x05 \x01(\bR\x15ignoreDeploymentState\x12/\n" + + "\x06config\x18\x06 \x01(\v2\x17.google.protobuf.StructR\x06config\x1aC\n" + + "\x15DeploymentStacksEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x1a\n" + + "\x18ProvisioningStateOptions\"J\n" + + "\x17ProvisioningStateResult\x12/\n" + + "\x05state\x18\x01 \x01(\v2\x19.azdext.ProvisioningStateR\x05state\"\xf2\x01\n" + + "\x11ProvisioningState\x12@\n" + + "\aoutputs\x18\x01 \x03(\v2&.azdext.ProvisioningState.OutputsEntryR\aoutputs\x12:\n" + + "\tresources\x18\x02 \x03(\v2\x1c.azdext.ProvisioningResourceR\tresources\x1a_\n" + + "\fOutputsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x129\n" + + "\x05value\x18\x02 \x01(\v2#.azdext.ProvisioningOutputParameterR\x05value:\x028\x01\"\xf3\x02\n" + + "\x16ProvisioningDeployment\x12N\n" + + "\n" + + "parameters\x18\x01 \x03(\v2..azdext.ProvisioningDeployment.ParametersEntryR\n" + + "parameters\x12E\n" + + "\aoutputs\x18\x02 \x03(\v2+.azdext.ProvisioningDeployment.OutputsEntryR\aoutputs\x1aa\n" + + "\x0fParametersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x128\n" + + "\x05value\x18\x02 \x01(\v2\".azdext.ProvisioningInputParameterR\x05value:\x028\x01\x1a_\n" + + "\fOutputsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x129\n" + + "\x05value\x18\x02 \x01(\v2#.azdext.ProvisioningOutputParameterR\x05value:\x028\x01\"\xa4\x01\n" + + "\x18ProvisioningDeployResult\x12>\n" + + "\n" + + "deployment\x18\x01 \x01(\v2\x1e.azdext.ProvisioningDeploymentR\n" + + "deployment\x12H\n" + + "\x0eskipped_reason\x18\x02 \x01(\x0e2!.azdext.ProvisioningSkippedReasonR\rskippedReason\"\xa2\x03\n" + + "\x1dProvisioningDeploymentPreview\x12\x18\n" + + "\asummary\x18\x01 \x01(\tR\asummary\x12U\n" + + "\n" + + "parameters\x18\x02 \x03(\v25.azdext.ProvisioningDeploymentPreview.ParametersEntryR\n" + + "parameters\x12L\n" + + "\aoutputs\x18\x03 \x03(\v22.azdext.ProvisioningDeploymentPreview.OutputsEntryR\aoutputs\x1aa\n" + + "\x0fParametersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x128\n" + + "\x05value\x18\x02 \x01(\v2\".azdext.ProvisioningInputParameterR\x05value:\x028\x01\x1a_\n" + + "\fOutputsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x129\n" + + "\x05value\x18\x02 \x01(\v2#.azdext.ProvisioningOutputParameterR\x05value:\x028\x01\"\\\n" + + "\x19ProvisioningPreviewResult\x12?\n" + + "\apreview\x18\x01 \x01(\v2%.azdext.ProvisioningDeploymentPreviewR\apreview\"H\n" + + "\x1aProvisioningDestroyOptions\x12\x14\n" + + "\x05force\x18\x01 \x01(\bR\x05force\x12\x14\n" + + "\x05purge\x18\x02 \x01(\bR\x05purge\"M\n" + + "\x19ProvisioningDestroyResult\x120\n" + + "\x14invalidated_env_keys\x18\x01 \x03(\tR\x12invalidatedEnvKeys\"k\n" + + "\x1aProvisioningInputParameter\x12\x12\n" + + "\x04type\x18\x01 \x01(\tR\x04type\x12#\n" + + "\rdefault_value\x18\x02 \x01(\tR\fdefaultValue\x12\x14\n" + + "\x05value\x18\x03 \x01(\tR\x05value\"G\n" + + "\x1bProvisioningOutputParameter\x12\x12\n" + + "\x04type\x18\x01 \x01(\tR\x04type\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value\"&\n" + + "\x14ProvisioningResource\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\xd7\x01\n" + + "\x15ProvisioningParameter\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + + "\x06secret\x18\x02 \x01(\bR\x06secret\x12\x14\n" + + "\x05value\x18\x03 \x01(\tR\x05value\x12&\n" + + "\x0fenv_var_mapping\x18\x04 \x03(\tR\renvVarMapping\x12!\n" + + "\flocal_prompt\x18\x05 \x01(\bR\vlocalPrompt\x121\n" + + "\x15using_env_var_mapping\x18\x06 \x01(\bR\x12usingEnvVarMapping\"t\n" + + "\x1bProvisioningProgressMessage\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\x12\x1c\n" + + "\ttimestamp\x18\x03 \x01(\x03R\ttimestamp*z\n" + + "\x19ProvisioningSkippedReason\x12+\n" + + "'PROVISIONING_SKIPPED_REASON_UNSPECIFIED\x10\x00\x120\n" + + ",PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE\x10\x012]\n" + + "\x13ProvisioningService\x12F\n" + + "\x06Stream\x12\x1b.azdext.ProvisioningMessage\x1a\x1b.azdext.ProvisioningMessage(\x010\x01B/Z-github.com/azure/azure-dev/cli/azd/pkg/azdextb\x06proto3" + +var ( + file_provisioning_proto_rawDescOnce sync.Once + file_provisioning_proto_rawDescData []byte +) + +func file_provisioning_proto_rawDescGZIP() []byte { + file_provisioning_proto_rawDescOnce.Do(func() { + file_provisioning_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_provisioning_proto_rawDesc), len(file_provisioning_proto_rawDesc))) + }) + return file_provisioning_proto_rawDescData +} + +var file_provisioning_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_provisioning_proto_msgTypes = make([]protoimpl.MessageInfo, 38) +var file_provisioning_proto_goTypes = []any{ + (ProvisioningSkippedReason)(0), // 0: azdext.ProvisioningSkippedReason + (*ProvisioningMessage)(nil), // 1: azdext.ProvisioningMessage + (*RegisterProvisioningProviderRequest)(nil), // 2: azdext.RegisterProvisioningProviderRequest + (*RegisterProvisioningProviderResponse)(nil), // 3: azdext.RegisterProvisioningProviderResponse + (*ProvisioningInitializeRequest)(nil), // 4: azdext.ProvisioningInitializeRequest + (*ProvisioningInitializeResponse)(nil), // 5: azdext.ProvisioningInitializeResponse + (*ProvisioningStateRequest)(nil), // 6: azdext.ProvisioningStateRequest + (*ProvisioningStateResponse)(nil), // 7: azdext.ProvisioningStateResponse + (*ProvisioningDeployRequest)(nil), // 8: azdext.ProvisioningDeployRequest + (*ProvisioningDeployResponse)(nil), // 9: azdext.ProvisioningDeployResponse + (*ProvisioningPreviewRequest)(nil), // 10: azdext.ProvisioningPreviewRequest + (*ProvisioningPreviewResponse)(nil), // 11: azdext.ProvisioningPreviewResponse + (*ProvisioningDestroyRequest)(nil), // 12: azdext.ProvisioningDestroyRequest + (*ProvisioningDestroyResponse)(nil), // 13: azdext.ProvisioningDestroyResponse + (*ProvisioningEnsureEnvRequest)(nil), // 14: azdext.ProvisioningEnsureEnvRequest + (*ProvisioningEnsureEnvResponse)(nil), // 15: azdext.ProvisioningEnsureEnvResponse + (*ProvisioningParametersRequest)(nil), // 16: azdext.ProvisioningParametersRequest + (*ProvisioningParametersResponse)(nil), // 17: azdext.ProvisioningParametersResponse + (*ProvisioningOptions)(nil), // 18: azdext.ProvisioningOptions + (*ProvisioningStateOptions)(nil), // 19: azdext.ProvisioningStateOptions + (*ProvisioningStateResult)(nil), // 20: azdext.ProvisioningStateResult + (*ProvisioningState)(nil), // 21: azdext.ProvisioningState + (*ProvisioningDeployment)(nil), // 22: azdext.ProvisioningDeployment + (*ProvisioningDeployResult)(nil), // 23: azdext.ProvisioningDeployResult + (*ProvisioningDeploymentPreview)(nil), // 24: azdext.ProvisioningDeploymentPreview + (*ProvisioningPreviewResult)(nil), // 25: azdext.ProvisioningPreviewResult + (*ProvisioningDestroyOptions)(nil), // 26: azdext.ProvisioningDestroyOptions + (*ProvisioningDestroyResult)(nil), // 27: azdext.ProvisioningDestroyResult + (*ProvisioningInputParameter)(nil), // 28: azdext.ProvisioningInputParameter + (*ProvisioningOutputParameter)(nil), // 29: azdext.ProvisioningOutputParameter + (*ProvisioningResource)(nil), // 30: azdext.ProvisioningResource + (*ProvisioningParameter)(nil), // 31: azdext.ProvisioningParameter + (*ProvisioningProgressMessage)(nil), // 32: azdext.ProvisioningProgressMessage + nil, // 33: azdext.ProvisioningOptions.DeploymentStacksEntry + nil, // 34: azdext.ProvisioningState.OutputsEntry + nil, // 35: azdext.ProvisioningDeployment.ParametersEntry + nil, // 36: azdext.ProvisioningDeployment.OutputsEntry + nil, // 37: azdext.ProvisioningDeploymentPreview.ParametersEntry + nil, // 38: azdext.ProvisioningDeploymentPreview.OutputsEntry + (*ExtensionError)(nil), // 39: azdext.ExtensionError + (*structpb.Struct)(nil), // 40: google.protobuf.Struct +} +var file_provisioning_proto_depIdxs = []int32{ + 39, // 0: azdext.ProvisioningMessage.error:type_name -> azdext.ExtensionError + 2, // 1: azdext.ProvisioningMessage.register_provisioning_provider_request:type_name -> azdext.RegisterProvisioningProviderRequest + 3, // 2: azdext.ProvisioningMessage.register_provisioning_provider_response:type_name -> azdext.RegisterProvisioningProviderResponse + 4, // 3: azdext.ProvisioningMessage.initialize_request:type_name -> azdext.ProvisioningInitializeRequest + 5, // 4: azdext.ProvisioningMessage.initialize_response:type_name -> azdext.ProvisioningInitializeResponse + 6, // 5: azdext.ProvisioningMessage.state_request:type_name -> azdext.ProvisioningStateRequest + 7, // 6: azdext.ProvisioningMessage.state_response:type_name -> azdext.ProvisioningStateResponse + 8, // 7: azdext.ProvisioningMessage.deploy_request:type_name -> azdext.ProvisioningDeployRequest + 9, // 8: azdext.ProvisioningMessage.deploy_response:type_name -> azdext.ProvisioningDeployResponse + 10, // 9: azdext.ProvisioningMessage.preview_request:type_name -> azdext.ProvisioningPreviewRequest + 11, // 10: azdext.ProvisioningMessage.preview_response:type_name -> azdext.ProvisioningPreviewResponse + 12, // 11: azdext.ProvisioningMessage.destroy_request:type_name -> azdext.ProvisioningDestroyRequest + 13, // 12: azdext.ProvisioningMessage.destroy_response:type_name -> azdext.ProvisioningDestroyResponse + 14, // 13: azdext.ProvisioningMessage.ensure_env_request:type_name -> azdext.ProvisioningEnsureEnvRequest + 15, // 14: azdext.ProvisioningMessage.ensure_env_response:type_name -> azdext.ProvisioningEnsureEnvResponse + 16, // 15: azdext.ProvisioningMessage.parameters_request:type_name -> azdext.ProvisioningParametersRequest + 17, // 16: azdext.ProvisioningMessage.parameters_response:type_name -> azdext.ProvisioningParametersResponse + 32, // 17: azdext.ProvisioningMessage.progress_message:type_name -> azdext.ProvisioningProgressMessage + 18, // 18: azdext.ProvisioningInitializeRequest.options:type_name -> azdext.ProvisioningOptions + 19, // 19: azdext.ProvisioningStateRequest.options:type_name -> azdext.ProvisioningStateOptions + 20, // 20: azdext.ProvisioningStateResponse.state_result:type_name -> azdext.ProvisioningStateResult + 23, // 21: azdext.ProvisioningDeployResponse.result:type_name -> azdext.ProvisioningDeployResult + 25, // 22: azdext.ProvisioningPreviewResponse.result:type_name -> azdext.ProvisioningPreviewResult + 26, // 23: azdext.ProvisioningDestroyRequest.options:type_name -> azdext.ProvisioningDestroyOptions + 27, // 24: azdext.ProvisioningDestroyResponse.result:type_name -> azdext.ProvisioningDestroyResult + 31, // 25: azdext.ProvisioningParametersResponse.parameters:type_name -> azdext.ProvisioningParameter + 33, // 26: azdext.ProvisioningOptions.deployment_stacks:type_name -> azdext.ProvisioningOptions.DeploymentStacksEntry + 40, // 27: azdext.ProvisioningOptions.config:type_name -> google.protobuf.Struct + 21, // 28: azdext.ProvisioningStateResult.state:type_name -> azdext.ProvisioningState + 34, // 29: azdext.ProvisioningState.outputs:type_name -> azdext.ProvisioningState.OutputsEntry + 30, // 30: azdext.ProvisioningState.resources:type_name -> azdext.ProvisioningResource + 35, // 31: azdext.ProvisioningDeployment.parameters:type_name -> azdext.ProvisioningDeployment.ParametersEntry + 36, // 32: azdext.ProvisioningDeployment.outputs:type_name -> azdext.ProvisioningDeployment.OutputsEntry + 22, // 33: azdext.ProvisioningDeployResult.deployment:type_name -> azdext.ProvisioningDeployment + 0, // 34: azdext.ProvisioningDeployResult.skipped_reason:type_name -> azdext.ProvisioningSkippedReason + 37, // 35: azdext.ProvisioningDeploymentPreview.parameters:type_name -> azdext.ProvisioningDeploymentPreview.ParametersEntry + 38, // 36: azdext.ProvisioningDeploymentPreview.outputs:type_name -> azdext.ProvisioningDeploymentPreview.OutputsEntry + 24, // 37: azdext.ProvisioningPreviewResult.preview:type_name -> azdext.ProvisioningDeploymentPreview + 29, // 38: azdext.ProvisioningState.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 28, // 39: azdext.ProvisioningDeployment.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter + 29, // 40: azdext.ProvisioningDeployment.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 28, // 41: azdext.ProvisioningDeploymentPreview.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter + 29, // 42: azdext.ProvisioningDeploymentPreview.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 1, // 43: azdext.ProvisioningService.Stream:input_type -> azdext.ProvisioningMessage + 1, // 44: azdext.ProvisioningService.Stream:output_type -> azdext.ProvisioningMessage + 44, // [44:45] is the sub-list for method output_type + 43, // [43:44] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name +} + +func init() { file_provisioning_proto_init() } +func file_provisioning_proto_init() { + if File_provisioning_proto != nil { + return + } + file_errors_proto_init() + file_provisioning_proto_msgTypes[0].OneofWrappers = []any{ + (*ProvisioningMessage_RegisterProvisioningProviderRequest)(nil), + (*ProvisioningMessage_RegisterProvisioningProviderResponse)(nil), + (*ProvisioningMessage_InitializeRequest)(nil), + (*ProvisioningMessage_InitializeResponse)(nil), + (*ProvisioningMessage_StateRequest)(nil), + (*ProvisioningMessage_StateResponse)(nil), + (*ProvisioningMessage_DeployRequest)(nil), + (*ProvisioningMessage_DeployResponse)(nil), + (*ProvisioningMessage_PreviewRequest)(nil), + (*ProvisioningMessage_PreviewResponse)(nil), + (*ProvisioningMessage_DestroyRequest)(nil), + (*ProvisioningMessage_DestroyResponse)(nil), + (*ProvisioningMessage_EnsureEnvRequest)(nil), + (*ProvisioningMessage_EnsureEnvResponse)(nil), + (*ProvisioningMessage_ParametersRequest)(nil), + (*ProvisioningMessage_ParametersResponse)(nil), + (*ProvisioningMessage_ProgressMessage)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_provisioning_proto_rawDesc), len(file_provisioning_proto_rawDesc)), + NumEnums: 1, + NumMessages: 38, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_provisioning_proto_goTypes, + DependencyIndexes: file_provisioning_proto_depIdxs, + EnumInfos: file_provisioning_proto_enumTypes, + MessageInfos: file_provisioning_proto_msgTypes, + }.Build() + File_provisioning_proto = out.File + file_provisioning_proto_goTypes = nil + file_provisioning_proto_depIdxs = nil +} diff --git a/cli/azd/pkg/azdext/provisioning_envelope.go b/cli/azd/pkg/azdext/provisioning_envelope.go new file mode 100644 index 00000000000..4b3d4a47418 --- /dev/null +++ b/cli/azd/pkg/azdext/provisioning_envelope.go @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package azdext + +import ( + "context" + "time" + + "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" +) + +// ProvisioningEnvelope provides message operations for ProvisioningMessage. +// It implements the grpcbroker.MessageEnvelope interface. +type ProvisioningEnvelope struct{} + +// NewProvisioningEnvelope creates a new ProvisioningEnvelope instance. +func NewProvisioningEnvelope() *ProvisioningEnvelope { + return &ProvisioningEnvelope{} +} + +// Verify interface implementation at compile time +var _ grpcbroker.MessageEnvelope[ProvisioningMessage] = (*ProvisioningEnvelope)(nil) + +// GetRequestId returns the request ID from the message. +func (ops *ProvisioningEnvelope) GetRequestId(ctx context.Context, msg *ProvisioningMessage) string { + return msg.RequestId +} + +// SetRequestId sets the request ID on the message. +func (ops *ProvisioningEnvelope) SetRequestId(ctx context.Context, msg *ProvisioningMessage, id string) { + msg.RequestId = id +} + +// GetError returns the error from the message as a Go error type. +// It returns a typed error based on the ErrorOrigin that preserves structured information for telemetry. +func (ops *ProvisioningEnvelope) GetError(msg *ProvisioningMessage) error { + return UnwrapError(msg.Error) +} + +// SetError sets an error on the message. +// It detects the error type and populates the appropriate source details. +func (ops *ProvisioningEnvelope) SetError(msg *ProvisioningMessage, err error) { + msg.Error = WrapError(err) +} + +// GetInnerMessage returns the inner message from the oneof field. +func (ops *ProvisioningEnvelope) GetInnerMessage(msg *ProvisioningMessage) any { + switch m := msg.MessageType.(type) { + case *ProvisioningMessage_RegisterProvisioningProviderRequest: + return m.RegisterProvisioningProviderRequest + case *ProvisioningMessage_RegisterProvisioningProviderResponse: + return m.RegisterProvisioningProviderResponse + case *ProvisioningMessage_InitializeRequest: + return m.InitializeRequest + case *ProvisioningMessage_InitializeResponse: + return m.InitializeResponse + case *ProvisioningMessage_StateRequest: + return m.StateRequest + case *ProvisioningMessage_StateResponse: + return m.StateResponse + case *ProvisioningMessage_DeployRequest: + return m.DeployRequest + case *ProvisioningMessage_DeployResponse: + return m.DeployResponse + case *ProvisioningMessage_PreviewRequest: + return m.PreviewRequest + case *ProvisioningMessage_PreviewResponse: + return m.PreviewResponse + case *ProvisioningMessage_DestroyRequest: + return m.DestroyRequest + case *ProvisioningMessage_DestroyResponse: + return m.DestroyResponse + case *ProvisioningMessage_EnsureEnvRequest: + return m.EnsureEnvRequest + case *ProvisioningMessage_EnsureEnvResponse: + return m.EnsureEnvResponse + case *ProvisioningMessage_ParametersRequest: + return m.ParametersRequest + case *ProvisioningMessage_ParametersResponse: + return m.ParametersResponse + case *ProvisioningMessage_ProgressMessage: + return m.ProgressMessage + default: + return nil + } +} + +// IsProgressMessage returns true if the message contains a progress message. +func (ops *ProvisioningEnvelope) IsProgressMessage(msg *ProvisioningMessage) bool { + return msg.GetProgressMessage() != nil +} + +// GetProgressMessage extracts the progress message text from a progress message. +// Returns empty string if the message is not a progress message. +func (ops *ProvisioningEnvelope) GetProgressMessage(msg *ProvisioningMessage) string { + if progressMsg := msg.GetProgressMessage(); progressMsg != nil { + return progressMsg.GetMessage() + } + return "" +} + +// CreateProgressMessage creates a new progress message envelope with the given text. +// This is used by server-side handlers to send progress updates back to clients. +func (ops *ProvisioningEnvelope) CreateProgressMessage( + requestId string, + message string, +) *ProvisioningMessage { + return &ProvisioningMessage{ + RequestId: requestId, + MessageType: &ProvisioningMessage_ProgressMessage{ + ProgressMessage: &ProvisioningProgressMessage{ + RequestId: requestId, + Message: message, + Timestamp: time.Now().UnixMilli(), + }, + }, + } +} diff --git a/cli/azd/pkg/azdext/provisioning_grpc.pb.go b/cli/azd/pkg/azdext/provisioning_grpc.pb.go new file mode 100644 index 00000000000..ef49a142324 --- /dev/null +++ b/cli/azd/pkg/azdext/provisioning_grpc.pb.go @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v6.32.1 +// source: provisioning.proto + +package azdext + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + ProvisioningService_Stream_FullMethodName = "/azdext.ProvisioningService/Stream" +) + +// ProvisioningServiceClient is the client API for ProvisioningService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ProvisioningServiceClient interface { + // Bidirectional stream for provisioning requests and responses + Stream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProvisioningMessage, ProvisioningMessage], error) +} + +type provisioningServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewProvisioningServiceClient(cc grpc.ClientConnInterface) ProvisioningServiceClient { + return &provisioningServiceClient{cc} +} + +func (c *provisioningServiceClient) Stream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProvisioningMessage, ProvisioningMessage], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &ProvisioningService_ServiceDesc.Streams[0], ProvisioningService_Stream_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[ProvisioningMessage, ProvisioningMessage]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type ProvisioningService_StreamClient = grpc.BidiStreamingClient[ProvisioningMessage, ProvisioningMessage] + +// ProvisioningServiceServer is the server API for ProvisioningService service. +// All implementations must embed UnimplementedProvisioningServiceServer +// for forward compatibility. +type ProvisioningServiceServer interface { + // Bidirectional stream for provisioning requests and responses + Stream(grpc.BidiStreamingServer[ProvisioningMessage, ProvisioningMessage]) error + mustEmbedUnimplementedProvisioningServiceServer() +} + +// UnimplementedProvisioningServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedProvisioningServiceServer struct{} + +func (UnimplementedProvisioningServiceServer) Stream(grpc.BidiStreamingServer[ProvisioningMessage, ProvisioningMessage]) error { + return status.Errorf(codes.Unimplemented, "method Stream not implemented") +} +func (UnimplementedProvisioningServiceServer) mustEmbedUnimplementedProvisioningServiceServer() {} +func (UnimplementedProvisioningServiceServer) testEmbeddedByValue() {} + +// UnsafeProvisioningServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ProvisioningServiceServer will +// result in compilation errors. +type UnsafeProvisioningServiceServer interface { + mustEmbedUnimplementedProvisioningServiceServer() +} + +func RegisterProvisioningServiceServer(s grpc.ServiceRegistrar, srv ProvisioningServiceServer) { + // If the following call pancis, it indicates UnimplementedProvisioningServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&ProvisioningService_ServiceDesc, srv) +} + +func _ProvisioningService_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(ProvisioningServiceServer).Stream(&grpc.GenericServerStream[ProvisioningMessage, ProvisioningMessage]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type ProvisioningService_StreamServer = grpc.BidiStreamingServer[ProvisioningMessage, ProvisioningMessage] + +// ProvisioningService_ServiceDesc is the grpc.ServiceDesc for ProvisioningService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ProvisioningService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "azdext.ProvisioningService", + HandlerType: (*ProvisioningServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Stream", + Handler: _ProvisioningService_Stream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "provisioning.proto", +} diff --git a/cli/azd/pkg/azdext/provisioning_manager.go b/cli/azd/pkg/azdext/provisioning_manager.go new file mode 100644 index 00000000000..01c645a9aab --- /dev/null +++ b/cli/azd/pkg/azdext/provisioning_manager.go @@ -0,0 +1,363 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package azdext + +import ( + "context" + "fmt" + "log" + "sync" + + "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" + "github.com/google/uuid" +) + +// ProvisioningProvider defines the interface for provisioning logic on the extension side. +type ProvisioningProvider interface { + Initialize(ctx context.Context, projectPath string, options *ProvisioningOptions) error + State(ctx context.Context, options *ProvisioningStateOptions) (*ProvisioningStateResult, error) + Deploy(ctx context.Context, progress grpcbroker.ProgressFunc) (*ProvisioningDeployResult, error) + Preview(ctx context.Context, progress grpcbroker.ProgressFunc) (*ProvisioningPreviewResult, error) + Destroy( + ctx context.Context, + options *ProvisioningDestroyOptions, + progress grpcbroker.ProgressFunc, + ) (*ProvisioningDestroyResult, error) + EnsureEnv(ctx context.Context) error + Parameters(ctx context.Context) ([]*ProvisioningParameter, error) +} + +// ProvisioningProviderFactory describes a function that creates a provisioning provider instance. +type ProvisioningProviderFactory func() ProvisioningProvider + +// ProvisioningManager handles registration and provisioning request forwarding for a provider. +type ProvisioningManager struct { + extensionId string + client *AzdClient + broker *grpcbroker.MessageBroker[ProvisioningMessage] + provider ProvisioningProvider + providerName string + brokerLogger *log.Logger + + // Synchronization for concurrent access + mu sync.RWMutex +} + +// NewProvisioningManager creates a new ProvisioningManager for an AzdClient. +func NewProvisioningManager( + extensionId string, + client *AzdClient, + brokerLogger *log.Logger, +) *ProvisioningManager { + return &ProvisioningManager{ + extensionId: extensionId, + client: client, + brokerLogger: brokerLogger, + } +} + +// Close terminates the underlying gRPC stream if it's been initialized. +// This method is thread-safe for concurrent access. +func (m *ProvisioningManager) Close() error { + m.mu.Lock() + defer m.mu.Unlock() + + if m.broker != nil { + m.broker.Close() + m.broker = nil + } + + return nil +} + +// ensureStream initializes the broker and stream if they haven't been created yet. +// This method is thread-safe for concurrent access. +func (m *ProvisioningManager) ensureStream(ctx context.Context) error { + // Fast path with read lock + m.mu.RLock() + if m.broker != nil { + m.mu.RUnlock() + return nil + } + m.mu.RUnlock() + + // Slow path with write lock + m.mu.Lock() + defer m.mu.Unlock() + + // Double-check after acquiring write lock + if m.broker != nil { + return nil + } + + stream, err := m.client.Provisioning().Stream(ctx) + if err != nil { + return fmt.Errorf("failed to create provisioning stream: %w", err) + } + + // Create broker with client stream + envelope := &ProvisioningEnvelope{} + m.broker = grpcbroker.NewMessageBroker(stream, envelope, m.extensionId, m.brokerLogger) + + // Register handlers for incoming requests + if err := m.broker.On(m.onInitialize); err != nil { + return fmt.Errorf("failed to register initialize handler: %w", err) + } + if err := m.broker.On(m.onState); err != nil { + return fmt.Errorf("failed to register state handler: %w", err) + } + if err := m.broker.On(m.onDeploy); err != nil { + return fmt.Errorf("failed to register deploy handler: %w", err) + } + if err := m.broker.On(m.onPreview); err != nil { + return fmt.Errorf("failed to register preview handler: %w", err) + } + if err := m.broker.On(m.onDestroy); err != nil { + return fmt.Errorf("failed to register destroy handler: %w", err) + } + if err := m.broker.On(m.onEnsureEnv); err != nil { + return fmt.Errorf("failed to register ensure env handler: %w", err) + } + if err := m.broker.On(m.onParameters); err != nil { + return fmt.Errorf("failed to register parameters handler: %w", err) + } + + return nil +} + +// Register registers the provider with the server, waits for the response, +// then starts background handling of provisioning requests. +func (m *ProvisioningManager) Register( + ctx context.Context, + factory ProvisioningProviderFactory, + providerName string, +) error { + if err := m.ensureStream(ctx); err != nil { + return err + } + + m.mu.Lock() + m.providerName = providerName + m.mu.Unlock() + + registerReq := &ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &ProvisioningMessage_RegisterProvisioningProviderRequest{ + RegisterProvisioningProviderRequest: &RegisterProvisioningProviderRequest{ + Name: providerName, + }, + }, + } + + resp, err := m.broker.SendAndWait(ctx, registerReq) + if err != nil { + return fmt.Errorf("provisioning provider registration failed: %w", err) + } + + if resp.GetRegisterProvisioningProviderResponse() == nil { + return fmt.Errorf( + "expected RegisterProvisioningProviderResponse, got %T", + resp.GetMessageType(), + ) + } + + // Store the factory for later use during onInitialize + m.mu.Lock() + m.provider = factory() + m.mu.Unlock() + + return nil +} + +// Receive starts the broker's message dispatcher and blocks until the stream completes. +func (m *ProvisioningManager) Receive(ctx context.Context) error { + if err := m.ensureStream(ctx); err != nil { + return err + } + + return m.broker.Run(ctx) +} + +// Ready blocks until the message broker starts receiving messages or the context is cancelled. +func (m *ProvisioningManager) Ready(ctx context.Context) error { + if err := m.ensureStream(ctx); err != nil { + return err + } + + return m.broker.Ready(ctx) +} + +// Handler methods - these are registered with the broker to handle incoming requests + +// onInitialize handles initialization requests from the server +func (m *ProvisioningManager) onInitialize( + ctx context.Context, + req *ProvisioningInitializeRequest, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + err := provider.Initialize(ctx, req.GetProjectPath(), req.GetOptions()) + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_InitializeResponse{ + InitializeResponse: &ProvisioningInitializeResponse{}, + }, + }, err +} + +// onState handles state requests +func (m *ProvisioningManager) onState( + ctx context.Context, + req *ProvisioningStateRequest, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + result, err := provider.State(ctx, req.GetOptions()) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_StateResponse{ + StateResponse: &ProvisioningStateResponse{StateResult: result}, + }, + }, nil +} + +// onDeploy handles deploy requests with progress reporting +func (m *ProvisioningManager) onDeploy( + ctx context.Context, + req *ProvisioningDeployRequest, + progress grpcbroker.ProgressFunc, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + result, err := provider.Deploy(ctx, progress) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_DeployResponse{ + DeployResponse: &ProvisioningDeployResponse{Result: result}, + }, + }, nil +} + +// onPreview handles preview requests with progress reporting +func (m *ProvisioningManager) onPreview( + ctx context.Context, + req *ProvisioningPreviewRequest, + progress grpcbroker.ProgressFunc, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + result, err := provider.Preview(ctx, progress) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_PreviewResponse{ + PreviewResponse: &ProvisioningPreviewResponse{Result: result}, + }, + }, nil +} + +// onDestroy handles destroy requests with progress reporting +func (m *ProvisioningManager) onDestroy( + ctx context.Context, + req *ProvisioningDestroyRequest, + progress grpcbroker.ProgressFunc, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + result, err := provider.Destroy(ctx, req.GetOptions(), progress) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_DestroyResponse{ + DestroyResponse: &ProvisioningDestroyResponse{Result: result}, + }, + }, nil +} + +// onEnsureEnv handles ensure env requests +func (m *ProvisioningManager) onEnsureEnv( + ctx context.Context, + req *ProvisioningEnsureEnvRequest, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + err := provider.EnsureEnv(ctx) + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_EnsureEnvResponse{ + EnsureEnvResponse: &ProvisioningEnsureEnvResponse{}, + }, + }, err +} + +// onParameters handles parameters requests +func (m *ProvisioningManager) onParameters( + ctx context.Context, + req *ProvisioningParametersRequest, +) (*ProvisioningMessage, error) { + m.mu.RLock() + provider := m.provider + m.mu.RUnlock() + + if provider == nil { + return nil, fmt.Errorf("provisioning provider not initialized") + } + + params, err := provider.Parameters(ctx) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_ParametersResponse{ + ParametersResponse: &ProvisioningParametersResponse{Parameters: params}, + }, + }, nil +} diff --git a/cli/azd/pkg/extensions/registry.go b/cli/azd/pkg/extensions/registry.go index e18f226f43b..1aba610bd0a 100644 --- a/cli/azd/pkg/extensions/registry.go +++ b/cli/azd/pkg/extensions/registry.go @@ -44,6 +44,8 @@ const ( FrameworkServiceProviderCapability CapabilityType = "framework-service-provider" // Metadata capability enables extensions to provide comprehensive metadata about their commands and capabilities MetadataCapability CapabilityType = "metadata" + // Provision provider enables extensions to provide a custom provisioning experience + ProvisioningProviderCapability CapabilityType = "provisioning-provider" ) type ProviderType string diff --git a/cli/azd/pkg/infra/provisioning/provider.go b/cli/azd/pkg/infra/provisioning/provider.go index 91be9b8f76e..cf7ea0cbb8b 100644 --- a/cli/azd/pkg/infra/provisioning/provider.go +++ b/cli/azd/pkg/infra/provisioning/provider.go @@ -40,6 +40,8 @@ type Options struct { Module string `yaml:"module,omitempty"` Name string `yaml:"name,omitempty"` DeploymentStacks map[string]any `yaml:"deploymentStacks,omitempty"` + // Config holds provider-specific configuration options + Config map[string]any `yaml:"config,omitempty"` // Provisioning options for each individually defined layer. Layers []Options `yaml:"layers,omitempty"` diff --git a/cli/azd/pkg/infra/provisioning/provisioning.go b/cli/azd/pkg/infra/provisioning/provisioning.go index 5a8755cef03..c4f7e85deb3 100644 --- a/cli/azd/pkg/infra/provisioning/provisioning.go +++ b/cli/azd/pkg/infra/provisioning/provisioning.go @@ -53,12 +53,5 @@ func NewEnvRefreshResultFromState(state *State) contracts.EnvRefreshResult { // Parses the specified IaC Provider to ensure whether it is valid or not // Defaults to `Bicep` if no provider is specified func ParseProvider(kind ProviderKind) (ProviderKind, error) { - switch kind { - // For the time being we need to include `Test` here for the unit tests to work as expected - // App builds will pass this test but fail resolving the provider since `Test` won't be registered in the container - case NotSpecified, Bicep, Terraform, Test: - return kind, nil - } - - return ProviderKind(""), fmt.Errorf("unsupported IaC provider '%s'", kind) + return kind, nil } diff --git a/cli/azd/pkg/infra/provisioning/provisioning_test.go b/cli/azd/pkg/infra/provisioning/provisioning_test.go index 74f6c7a9e65..dd81ce9e446 100644 --- a/cli/azd/pkg/infra/provisioning/provisioning_test.go +++ b/cli/azd/pkg/infra/provisioning/provisioning_test.go @@ -98,10 +98,9 @@ func TestNewEnvRefreshResultFromState(t *testing.T) { func TestParseProvider(t *testing.T) { tests := []struct { - name string - kind ProviderKind - expected ProviderKind - expectErr bool + name string + kind ProviderKind + expected ProviderKind }{ { name: "empty defaults to NotSpecified", @@ -124,34 +123,27 @@ func TestParseProvider(t *testing.T) { expected: Test, }, { - name: "unsupported provider", - kind: ProviderKind("invalid"), - expectErr: true, + name: "custom provider is accepted", + kind: ProviderKind("custom-ext"), + expected: ProviderKind("custom-ext"), }, { - name: "pulumi is unsupported", - kind: Pulumi, - expectErr: true, + name: "pulumi is accepted", + kind: Pulumi, + expected: Pulumi, }, { - name: "arm is unsupported", - kind: Arm, - expectErr: true, + name: "arm is accepted", + kind: Arm, + expected: Arm, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := ParseProvider(tt.kind) - if tt.expectErr { - require.Error(t, err) - assert.Contains( - t, err.Error(), "unsupported IaC provider", - ) - } else { - require.NoError(t, err) - assert.Equal(t, tt.expected, result) - } + require.NoError(t, err) + assert.Equal(t, tt.expected, result) }) } } From 03855a9130f0ff91a0045f62274a7c1fe7cc6fb0 Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Thu, 2 Apr 2026 14:06:45 -0700 Subject: [PATCH 2/5] fix: simplify parameter conversion to avoid redundant assignment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../grpcserver/external_provisioning_provider.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cli/azd/internal/grpcserver/external_provisioning_provider.go b/cli/azd/internal/grpcserver/external_provisioning_provider.go index 6c37cbeb768..579f6be3abf 100644 --- a/cli/azd/internal/grpcserver/external_provisioning_provider.go +++ b/cli/azd/internal/grpcserver/external_provisioning_provider.go @@ -302,17 +302,14 @@ func convertFromProtoDeployResult( } for k, v := range result.Deployment.Parameters { - deployment.Parameters[k] = provisioning.InputParameter{ + param := provisioning.InputParameter{ Type: v.Type, Value: v.Value, } if v.DefaultValue != "" { - deployment.Parameters[k] = provisioning.InputParameter{ - Type: v.Type, - DefaultValue: v.DefaultValue, - Value: v.Value, - } + param.DefaultValue = v.DefaultValue } + deployment.Parameters[k] = param } for k, v := range result.Deployment.Outputs { From 28323a86c0d6a5d7f5c9228669b992640ffc4e8f Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Thu, 2 Apr 2026 15:25:13 -0700 Subject: [PATCH 3/5] fix: Address code review findings and add demo provisioning provider Review fixes: - Add empty provider name validation in registration - Fix swallowed structpb.NewStruct error in config conversion - Extract getProvider() helper to eliminate DRY violation in handlers - Add error context wrapping to all ExternalProvisioningProvider methods - Return empty slices instead of nil from PlannedOutputs/Parameters - Use direct indexing in convertFromProtoParameters - Consolidate Register() lock acquisitions into single scope Demo extension: - Add DemoProvisioningProvider to microsoft.azd.demo extension - Register as 'demo' provider with WithProvisioningProvider() - Add provisioning-provider to extension capabilities Test fixes: - Add ProvisioningProviderCapability to ValidCapabilities list - Update TestListenCapabilities assertion count - Add capability to validation test coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../middleware/middleware_coverage_test.go | 3 +- .../microsoft.azd.demo/extension.yaml | 4 + .../microsoft.azd.demo/internal/cmd/listen.go | 3 + .../internal/project/provisioning_demo.go | 252 ++++++++++++++++++ .../external_provisioning_provider.go | 74 +++-- .../grpcserver/provisioning_service.go | 7 + cli/azd/pkg/azdext/provisioning_manager.go | 132 +++++---- cli/azd/pkg/extensions/validate_registry.go | 1 + .../pkg/extensions/validate_registry_test.go | 1 + 9 files changed, 398 insertions(+), 79 deletions(-) create mode 100644 cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go diff --git a/cli/azd/cmd/middleware/middleware_coverage_test.go b/cli/azd/cmd/middleware/middleware_coverage_test.go index 4d3a5c4555f..bda087e8b9f 100644 --- a/cli/azd/cmd/middleware/middleware_coverage_test.go +++ b/cli/azd/cmd/middleware/middleware_coverage_test.go @@ -654,7 +654,8 @@ func TestListenCapabilities_ContainsExpectedValues(t *testing.T) { require.Contains(t, listenCapabilities, extensions.LifecycleEventsCapability) require.Contains(t, listenCapabilities, extensions.ServiceTargetProviderCapability) require.Contains(t, listenCapabilities, extensions.FrameworkServiceProviderCapability) - require.Len(t, listenCapabilities, 3) + require.Contains(t, listenCapabilities, extensions.ProvisioningProviderCapability) + require.Len(t, listenCapabilities, 4) } // --------------------------------------------------------------------------- diff --git a/cli/azd/extensions/microsoft.azd.demo/extension.yaml b/cli/azd/extensions/microsoft.azd.demo/extension.yaml index 05e570efb95..c9cc41b1be1 100644 --- a/cli/azd/extensions/microsoft.azd.demo/extension.yaml +++ b/cli/azd/extensions/microsoft.azd.demo/extension.yaml @@ -12,11 +12,15 @@ capabilities: - mcp-server - service-target-provider - framework-service-provider + - provisioning-provider - metadata providers: - name: demo type: service-target description: Deploys application components to demo + - name: demo + type: provisioning-provider + description: Provisions infrastructure using the demo provider examples: - name: context description: Displays the current `azd` project & environment context. diff --git a/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go b/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go index 3e33f030734..d4a936fde5a 100644 --- a/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go +++ b/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go @@ -35,6 +35,9 @@ func newListenCommand() *cobra.Command { WithFrameworkService("rust", func() azdext.FrameworkServiceProvider { return project.NewDemoFrameworkServiceProvider(azdClient) }). + WithProvisioningProvider("demo", func() azdext.ProvisioningProvider { + return project.NewDemoProvisioningProvider(azdClient) + }). WithProjectEventHandler("preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error { for i := 1; i <= 20; i++ { fmt.Printf("%d. Doing important work in extension...\n", i) diff --git a/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go b/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go new file mode 100644 index 00000000000..56ae9e00021 --- /dev/null +++ b/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go @@ -0,0 +1,252 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// cspell:ignore demostore + +package project + +import ( + "context" + "fmt" + "time" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" +) + +// Ensure DemoProvisioningProvider implements ProvisioningProvider interface +var _ azdext.ProvisioningProvider = &DemoProvisioningProvider{} + +// DemoProvisioningProvider is a demonstration implementation of a provisioning provider. +// This shows how to implement infrastructure provisioning support as an azd extension. +type DemoProvisioningProvider struct { + azdClient *azdext.AzdClient + projectPath string + options *azdext.ProvisioningOptions +} + +// NewDemoProvisioningProvider creates a new DemoProvisioningProvider instance +func NewDemoProvisioningProvider( + azdClient *azdext.AzdClient, +) azdext.ProvisioningProvider { + return &DemoProvisioningProvider{ + azdClient: azdClient, + } +} + +// Initialize initializes the provisioning provider with the project path and options +func (p *DemoProvisioningProvider) Initialize( + ctx context.Context, + projectPath string, + options *azdext.ProvisioningOptions, +) error { + p.projectPath = projectPath + p.options = options + + fmt.Printf( + "Demo provisioning provider initialized (project: %s, provider: %s)\n", + projectPath, + options.GetProvider(), + ) + + // Demonstrate calling back to azd to get environment info + envResponse, err := p.azdClient.Environment(). + GetCurrent(ctx, &azdext.EmptyRequest{}) + if err != nil { + fmt.Printf("Warning: could not get current environment: %v\n", err) + } else { + fmt.Printf( + "Current environment: %s\n", + envResponse.GetEnvironment().GetName(), + ) + } + + return nil +} + +// State returns the current provisioning state with sample outputs and resources +func (p *DemoProvisioningProvider) State( + ctx context.Context, + options *azdext.ProvisioningStateOptions, +) (*azdext.ProvisioningStateResult, error) { + fmt.Println("Demo provisioning provider: retrieving state") + time.Sleep(500 * time.Millisecond) + + return &azdext.ProvisioningStateResult{ + State: &azdext.ProvisioningState{ + Outputs: map[string]*azdext.ProvisioningOutputParameter{ + "DEMO_ENDPOINT": { + Type: "string", + Value: "https://demo-app.example.com", + }, + "DEMO_RESOURCE_GROUP": { + Type: "string", + Value: "rg-demo-dev", + }, + "DEMO_REGION": { + Type: "string", + Value: "eastus2", + }, + }, + Resources: []*azdext.ProvisioningResource{ + {Id: "/subscriptions/00000000-0000-0000-0000-000000000000" + + "/resourceGroups/rg-demo-dev"}, + {Id: "/subscriptions/00000000-0000-0000-0000-000000000000" + + "/resourceGroups/rg-demo-dev" + + "/providers/Microsoft.Web/sites/demo-app"}, + {Id: "/subscriptions/00000000-0000-0000-0000-000000000000" + + "/resourceGroups/rg-demo-dev" + + "/providers/Microsoft.Storage/storageAccounts/demostore"}, + }, + }, + }, nil +} + +// Deploy simulates a multi-step infrastructure deployment with progress updates +func (p *DemoProvisioningProvider) Deploy( + ctx context.Context, + progress grpcbroker.ProgressFunc, +) (*azdext.ProvisioningDeployResult, error) { + fmt.Println("Demo provisioning provider: starting deployment") + + progress("Preparing resources...") + time.Sleep(1 * time.Second) + + progress("Deploying infrastructure...") + time.Sleep(2 * time.Second) + + progress("Configuring endpoints...") + time.Sleep(1 * time.Second) + + progress("Deployment complete") + time.Sleep(500 * time.Millisecond) + + fmt.Println("Demo provisioning provider: deployment finished") + + return &azdext.ProvisioningDeployResult{ + Deployment: &azdext.ProvisioningDeployment{ + Parameters: map[string]*azdext.ProvisioningInputParameter{ + "location": { + Type: "string", + DefaultValue: "eastus2", + Value: "eastus2", + }, + "appServicePlanSku": { + Type: "string", + DefaultValue: "B1", + Value: "B1", + }, + }, + Outputs: map[string]*azdext.ProvisioningOutputParameter{ + "DEMO_ENDPOINT": { + Type: "string", + Value: "https://demo-app.example.com", + }, + "DEMO_RESOURCE_GROUP": { + Type: "string", + Value: "rg-demo-dev", + }, + }, + }, + }, nil +} + +// Preview returns a simulated preview of the deployment plan +func (p *DemoProvisioningProvider) Preview( + ctx context.Context, + progress grpcbroker.ProgressFunc, +) (*azdext.ProvisioningPreviewResult, error) { + fmt.Println("Demo provisioning provider: generating preview") + + progress("Analyzing current state...") + time.Sleep(1 * time.Second) + + progress("Computing deployment plan...") + time.Sleep(1 * time.Second) + + fmt.Println("Demo provisioning provider: preview generated") + + return &azdext.ProvisioningPreviewResult{ + Preview: &azdext.ProvisioningDeploymentPreview{ + Summary: "Demo deployment: 3 resources to create, " + + "0 to update, 0 to delete", + Parameters: map[string]*azdext.ProvisioningInputParameter{ + "location": { + Type: "string", + DefaultValue: "eastus2", + Value: "eastus2", + }, + }, + Outputs: map[string]*azdext.ProvisioningOutputParameter{ + "DEMO_ENDPOINT": { + Type: "string", + Value: "https://demo-app.example.com", + }, + }, + }, + }, nil +} + +// Destroy simulates infrastructure destruction with progress updates +func (p *DemoProvisioningProvider) Destroy( + ctx context.Context, + options *azdext.ProvisioningDestroyOptions, + progress grpcbroker.ProgressFunc, +) (*azdext.ProvisioningDestroyResult, error) { + fmt.Printf( + "Demo provisioning provider: starting destroy (force: %t, purge: %t)\n", + options.GetForce(), + options.GetPurge(), + ) + + progress("Identifying resources to destroy...") + time.Sleep(1 * time.Second) + + progress("Removing application resources...") + time.Sleep(2 * time.Second) + + progress("Cleaning up resource group...") + time.Sleep(1 * time.Second) + + progress("Destroy complete") + time.Sleep(500 * time.Millisecond) + + fmt.Println("Demo provisioning provider: destroy finished") + + return &azdext.ProvisioningDestroyResult{ + InvalidatedEnvKeys: []string{ + "DEMO_ENDPOINT", + "DEMO_RESOURCE_GROUP", + "DEMO_REGION", + }, + }, nil +} + +// EnsureEnv ensures the environment is properly configured for provisioning +func (p *DemoProvisioningProvider) EnsureEnv(ctx context.Context) error { + fmt.Println("Demo provisioning provider: ensuring environment") + return nil +} + +// Parameters returns sample provisioning parameters +func (p *DemoProvisioningProvider) Parameters( + ctx context.Context, +) ([]*azdext.ProvisioningParameter, error) { + fmt.Println("Demo provisioning provider: retrieving parameters") + + return []*azdext.ProvisioningParameter{ + { + Name: "location", + Value: "eastus2", + }, + { + Name: "adminPassword", + Secret: true, + }, + { + Name: "appName", + Value: "demo-app", + EnvVarMapping: []string{"AZURE_APP_NAME"}, + }, + }, nil +} diff --git a/cli/azd/internal/grpcserver/external_provisioning_provider.go b/cli/azd/internal/grpcserver/external_provisioning_provider.go index 579f6be3abf..8a086ec24d9 100644 --- a/cli/azd/internal/grpcserver/external_provisioning_provider.go +++ b/cli/azd/internal/grpcserver/external_provisioning_provider.go @@ -57,7 +57,12 @@ func (p *ExternalProvisioningProvider) Initialize( projectPath string, options provisioning.Options, ) error { - protoOptions := convertToProtoOptions(options) + protoOptions, err := convertToProtoOptions(options) + if err != nil { + return fmt.Errorf( + "failed to convert provisioning options: %w", err, + ) + } req := &azdext.ProvisioningMessage{ RequestId: uuid.NewString(), @@ -69,8 +74,14 @@ func (p *ExternalProvisioningProvider) Initialize( }, } - _, err := p.broker.SendAndWait(ctx, req) - return err + _, err = p.broker.SendAndWait(ctx, req) + if err != nil { + return fmt.Errorf( + "provisioning initialize failed: %w", err, + ) + } + + return nil } // State returns the current state of provisioned infrastructure. @@ -89,7 +100,9 @@ func (p *ExternalProvisioningProvider) State( resp, err := p.broker.SendAndWait(ctx, req) if err != nil { - return nil, err + return nil, fmt.Errorf( + "provisioning state failed: %w", err, + ) } stateResp := resp.GetStateResponse() @@ -115,7 +128,9 @@ func (p *ExternalProvisioningProvider) Deploy( log.Printf("provisioning progress: %s", msg) }) if err != nil { - return nil, err + return nil, fmt.Errorf( + "provisioning deploy failed: %w", err, + ) } deployResp := resp.GetDeployResponse() @@ -141,7 +156,9 @@ func (p *ExternalProvisioningProvider) Preview( log.Printf("provisioning preview progress: %s", msg) }) if err != nil { - return nil, err + return nil, fmt.Errorf( + "provisioning preview failed: %w", err, + ) } previewResp := resp.GetPreviewResponse() @@ -173,7 +190,9 @@ func (p *ExternalProvisioningProvider) Destroy( log.Printf("provisioning destroy progress: %s", msg) }) if err != nil { - return nil, err + return nil, fmt.Errorf( + "provisioning destroy failed: %w", err, + ) } destroyResp := resp.GetDestroyResponse() @@ -196,7 +215,13 @@ func (p *ExternalProvisioningProvider) EnsureEnv(ctx context.Context) error { } _, err := p.broker.SendAndWait(ctx, req) - return err + if err != nil { + return fmt.Errorf( + "provisioning ensure env failed: %w", err, + ) + } + + return nil } // Parameters returns the provisioning parameters. @@ -212,12 +237,14 @@ func (p *ExternalProvisioningProvider) Parameters( resp, err := p.broker.SendAndWait(ctx, req) if err != nil { - return nil, err + return nil, fmt.Errorf( + "provisioning parameters failed: %w", err, + ) } paramsResp := resp.GetParametersResponse() if paramsResp == nil { - return nil, nil + return []provisioning.Parameter{}, nil } return convertFromProtoParameters(paramsResp.Parameters), nil @@ -227,15 +254,17 @@ func (p *ExternalProvisioningProvider) Parameters( func (p *ExternalProvisioningProvider) PlannedOutputs( ctx context.Context, ) ([]provisioning.PlannedOutput, error) { - return nil, nil + return []provisioning.PlannedOutput{}, nil } // --- Conversion helpers --- func convertToProtoOptions( options provisioning.Options, -) *azdext.ProvisioningOptions { - deploymentStacks := make(map[string]string, len(options.DeploymentStacks)) +) (*azdext.ProvisioningOptions, error) { + deploymentStacks := make( + map[string]string, len(options.DeploymentStacks), + ) for k, v := range options.DeploymentStacks { deploymentStacks[k] = fmt.Sprintf("%v", v) } @@ -250,12 +279,17 @@ func convertToProtoOptions( // Convert Config to protobuf Struct if present if options.Config != nil { - if s, err := structpb.NewStruct(options.Config); err == nil { - protoOptions.Config = s + s, err := structpb.NewStruct(options.Config) + if err != nil { + return nil, fmt.Errorf( + "failed to convert config to protobuf struct: %w", + err, + ) } + protoOptions.Config = s } - return protoOptions + return protoOptions, nil } func convertFromProtoStateResult( @@ -351,16 +385,16 @@ func convertFromProtoPreviewResult( func convertFromProtoParameters( params []*azdext.ProvisioningParameter, ) []provisioning.Parameter { - result := make([]provisioning.Parameter, 0, len(params)) - for _, p := range params { - result = append(result, provisioning.Parameter{ + result := make([]provisioning.Parameter, len(params)) + for i, p := range params { + result[i] = provisioning.Parameter{ Name: p.Name, Secret: p.Secret, Value: p.Value, EnvVarMapping: p.EnvVarMapping, LocalPrompt: p.LocalPrompt, UsingEnvVarMapping: p.UsingEnvVarMapping, - }) + } } return result } diff --git a/cli/azd/internal/grpcserver/provisioning_service.go b/cli/azd/internal/grpcserver/provisioning_service.go index 590d2c5b98a..46c620c8a3b 100644 --- a/cli/azd/internal/grpcserver/provisioning_service.go +++ b/cli/azd/internal/grpcserver/provisioning_service.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "log" + "strings" "sync" "github.com/azure/azure-dev/cli/azd/pkg/azdext" @@ -115,6 +116,12 @@ func (s *ProvisioningService) onRegisterRequest( registeredProviderName *string, ) (*azdext.ProvisioningMessage, error) { providerName := req.GetName() + if strings.TrimSpace(providerName) == "" { + return nil, status.Errorf( + codes.InvalidArgument, "provider name cannot be empty", + ) + } + s.providerMapMu.Lock() defer s.providerMapMu.Unlock() diff --git a/cli/azd/pkg/azdext/provisioning_manager.go b/cli/azd/pkg/azdext/provisioning_manager.go index 01c645a9aab..dea43f3de2c 100644 --- a/cli/azd/pkg/azdext/provisioning_manager.go +++ b/cli/azd/pkg/azdext/provisioning_manager.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "strings" "sync" "github.com/azure/azure-dev/cli/azd/pkg/grpcbroker" @@ -133,14 +134,14 @@ func (m *ProvisioningManager) Register( factory ProvisioningProviderFactory, providerName string, ) error { + if strings.TrimSpace(providerName) == "" { + return fmt.Errorf("provisioning provider name cannot be empty") + } + if err := m.ensureStream(ctx); err != nil { return err } - m.mu.Lock() - m.providerName = providerName - m.mu.Unlock() - registerReq := &ProvisioningMessage{ RequestId: uuid.NewString(), MessageType: &ProvisioningMessage_RegisterProvisioningProviderRequest{ @@ -152,7 +153,9 @@ func (m *ProvisioningManager) Register( resp, err := m.broker.SendAndWait(ctx, registerReq) if err != nil { - return fmt.Errorf("provisioning provider registration failed: %w", err) + return fmt.Errorf( + "provisioning provider registration failed: %w", err, + ) } if resp.GetRegisterProvisioningProviderResponse() == nil { @@ -162,9 +165,12 @@ func (m *ProvisioningManager) Register( ) } - // Store the factory for later use during onInitialize + // Create provider and store with provider name in a single lock scope + provider := factory() + m.mu.Lock() - m.provider = factory() + m.providerName = providerName + m.provider = provider m.mu.Unlock() return nil @@ -188,22 +194,38 @@ func (m *ProvisioningManager) Ready(ctx context.Context) error { return m.broker.Ready(ctx) } -// Handler methods - these are registered with the broker to handle incoming requests +// Handler methods - these are registered with the broker to handle +// incoming requests + +// getProvider returns the provisioning provider, or an error if +// not initialized. +func (m *ProvisioningManager) getProvider() ( + ProvisioningProvider, error, +) { + m.mu.RLock() + defer m.mu.RUnlock() + + if m.provider == nil { + return nil, fmt.Errorf( + "provisioning provider not initialized", + ) + } + return m.provider, nil +} // onInitialize handles initialization requests from the server func (m *ProvisioningManager) onInitialize( ctx context.Context, req *ProvisioningInitializeRequest, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } - err := provider.Initialize(ctx, req.GetProjectPath(), req.GetOptions()) + err = provider.Initialize( + ctx, req.GetProjectPath(), req.GetOptions(), + ) return &ProvisioningMessage{ MessageType: &ProvisioningMessage_InitializeResponse{ @@ -217,12 +239,9 @@ func (m *ProvisioningManager) onState( ctx context.Context, req *ProvisioningStateRequest, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } result, err := provider.State(ctx, req.GetOptions()) @@ -232,7 +251,9 @@ func (m *ProvisioningManager) onState( return &ProvisioningMessage{ MessageType: &ProvisioningMessage_StateResponse{ - StateResponse: &ProvisioningStateResponse{StateResult: result}, + StateResponse: &ProvisioningStateResponse{ + StateResult: result, + }, }, }, nil } @@ -243,12 +264,9 @@ func (m *ProvisioningManager) onDeploy( req *ProvisioningDeployRequest, progress grpcbroker.ProgressFunc, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } result, err := provider.Deploy(ctx, progress) @@ -258,7 +276,9 @@ func (m *ProvisioningManager) onDeploy( return &ProvisioningMessage{ MessageType: &ProvisioningMessage_DeployResponse{ - DeployResponse: &ProvisioningDeployResponse{Result: result}, + DeployResponse: &ProvisioningDeployResponse{ + Result: result, + }, }, }, nil } @@ -269,12 +289,9 @@ func (m *ProvisioningManager) onPreview( req *ProvisioningPreviewRequest, progress grpcbroker.ProgressFunc, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } result, err := provider.Preview(ctx, progress) @@ -284,7 +301,9 @@ func (m *ProvisioningManager) onPreview( return &ProvisioningMessage{ MessageType: &ProvisioningMessage_PreviewResponse{ - PreviewResponse: &ProvisioningPreviewResponse{Result: result}, + PreviewResponse: &ProvisioningPreviewResponse{ + Result: result, + }, }, }, nil } @@ -295,22 +314,23 @@ func (m *ProvisioningManager) onDestroy( req *ProvisioningDestroyRequest, progress grpcbroker.ProgressFunc, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } - result, err := provider.Destroy(ctx, req.GetOptions(), progress) + result, err := provider.Destroy( + ctx, req.GetOptions(), progress, + ) if err != nil { return nil, err } return &ProvisioningMessage{ MessageType: &ProvisioningMessage_DestroyResponse{ - DestroyResponse: &ProvisioningDestroyResponse{Result: result}, + DestroyResponse: &ProvisioningDestroyResponse{ + Result: result, + }, }, }, nil } @@ -320,15 +340,12 @@ func (m *ProvisioningManager) onEnsureEnv( ctx context.Context, req *ProvisioningEnsureEnvRequest, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } - err := provider.EnsureEnv(ctx) + err = provider.EnsureEnv(ctx) return &ProvisioningMessage{ MessageType: &ProvisioningMessage_EnsureEnvResponse{ @@ -342,12 +359,9 @@ func (m *ProvisioningManager) onParameters( ctx context.Context, req *ProvisioningParametersRequest, ) (*ProvisioningMessage, error) { - m.mu.RLock() - provider := m.provider - m.mu.RUnlock() - - if provider == nil { - return nil, fmt.Errorf("provisioning provider not initialized") + provider, err := m.getProvider() + if err != nil { + return nil, err } params, err := provider.Parameters(ctx) @@ -357,7 +371,9 @@ func (m *ProvisioningManager) onParameters( return &ProvisioningMessage{ MessageType: &ProvisioningMessage_ParametersResponse{ - ParametersResponse: &ProvisioningParametersResponse{Parameters: params}, + ParametersResponse: &ProvisioningParametersResponse{ + Parameters: params, + }, }, }, nil } diff --git a/cli/azd/pkg/extensions/validate_registry.go b/cli/azd/pkg/extensions/validate_registry.go index 053fcb42dea..7343c691cdd 100644 --- a/cli/azd/pkg/extensions/validate_registry.go +++ b/cli/azd/pkg/extensions/validate_registry.go @@ -30,6 +30,7 @@ var ValidCapabilities = []CapabilityType{ ServiceTargetProviderCapability, FrameworkServiceProviderCapability, MetadataCapability, + ProvisioningProviderCapability, } // validChecksumAlgorithms defines the supported checksum algorithms. diff --git a/cli/azd/pkg/extensions/validate_registry_test.go b/cli/azd/pkg/extensions/validate_registry_test.go index e407a17261d..29b1eeb814a 100644 --- a/cli/azd/pkg/extensions/validate_registry_test.go +++ b/cli/azd/pkg/extensions/validate_registry_test.go @@ -492,6 +492,7 @@ func TestValidateExtension_AllValidCapabilities(t *testing.T) { ServiceTargetProviderCapability, FrameworkServiceProviderCapability, MetadataCapability, + ProvisioningProviderCapability, }, Artifacts: validArtifacts(), }, From d39a7a02b545996c799b94943c388b1ae18c599e Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Fri, 3 Apr 2026 14:25:08 -0700 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20Address=20PR=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20proto=20fields,=20PlannedOutputs,=20convert=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proto changes: - Add hint field to ProvisioningStateOptions for deployment lookup - Add name field to ProvisioningOptions for layer scoping - Add PlannedOutputs request/response messages Core changes: - Wire hint from StateOptions through adapter to extension - Wire Options.Name through convertToProtoOptions - Implement PlannedOutputs end-to-end (proto, envelope, manager, adapter) - Update ParseProvider doc comment to match current behavior - Add TODO comments for progress callback console integration - Add scope comment on preview resource-level changes Testing: - Add table-driven tests for all convert helper functions (17 test cases) Demo extension: - Implement PlannedOutputs in DemoProvisioningProvider Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../internal/project/provisioning_demo.go | 11 + cli/azd/grpc/proto/provisioning.proto | 19 +- .../external_provisioning_provider.go | 47 ++- .../external_provisioning_provider_test.go | 363 ++++++++++++++++++ cli/azd/pkg/azdext/models.pb.go | 4 +- cli/azd/pkg/azdext/provisioning.pb.go | 289 +++++++++++--- cli/azd/pkg/azdext/provisioning_envelope.go | 4 + cli/azd/pkg/azdext/provisioning_manager.go | 28 ++ .../pkg/infra/provisioning/provisioning.go | 4 +- 9 files changed, 711 insertions(+), 58 deletions(-) create mode 100644 cli/azd/internal/grpcserver/external_provisioning_provider_test.go diff --git a/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go b/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go index 56ae9e00021..a5cd09c71c0 100644 --- a/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go +++ b/cli/azd/extensions/microsoft.azd.demo/internal/project/provisioning_demo.go @@ -250,3 +250,14 @@ func (p *DemoProvisioningProvider) Parameters( }, }, nil } + +// PlannedOutputs returns the list of outputs this provider plans to produce +func (p *DemoProvisioningProvider) PlannedOutputs( + ctx context.Context, +) ([]*azdext.ProvisioningPlannedOutput, error) { + return []*azdext.ProvisioningPlannedOutput{ + {Name: "DEMO_ENDPOINT"}, + {Name: "DEMO_RESOURCE_GROUP"}, + {Name: "DEMO_REGION"}, + }, nil +} diff --git a/cli/azd/grpc/proto/provisioning.proto b/cli/azd/grpc/proto/provisioning.proto index 70bb27f938c..b17a62bdeed 100644 --- a/cli/azd/grpc/proto/provisioning.proto +++ b/cli/azd/grpc/proto/provisioning.proto @@ -36,6 +36,8 @@ message ProvisioningMessage { ProvisioningParametersRequest parameters_request = 16; ProvisioningParametersResponse parameters_response = 17; ProvisioningProgressMessage progress_message = 18; + ProvisioningPlannedOutputsRequest planned_outputs_request = 19; + ProvisioningPlannedOutputsResponse planned_outputs_response = 20; } } @@ -115,9 +117,12 @@ message ProvisioningOptions { map deployment_stacks = 4; bool ignore_deployment_state = 5; google.protobuf.Struct config = 6; + string name = 7; // Layer name for multi-layer deployments } -message ProvisioningStateOptions {} +message ProvisioningStateOptions { + string hint = 1; // Deployment name hint for state lookup +} message ProvisioningStateResult { ProvisioningState state = 1; @@ -191,3 +196,15 @@ message ProvisioningProgressMessage { string message = 2; int64 timestamp = 3; } + +// --- PlannedOutputs --- + +message ProvisioningPlannedOutputsRequest {} + +message ProvisioningPlannedOutputsResponse { + repeated ProvisioningPlannedOutput planned_outputs = 1; +} + +message ProvisioningPlannedOutput { + string name = 1; +} diff --git a/cli/azd/internal/grpcserver/external_provisioning_provider.go b/cli/azd/internal/grpcserver/external_provisioning_provider.go index 8a086ec24d9..1f54161af81 100644 --- a/cli/azd/internal/grpcserver/external_provisioning_provider.go +++ b/cli/azd/internal/grpcserver/external_provisioning_provider.go @@ -89,11 +89,18 @@ func (p *ExternalProvisioningProvider) State( ctx context.Context, options *provisioning.StateOptions, ) (*provisioning.StateResult, error) { + var hint string + if options != nil { + hint = options.Hint() + } + req := &azdext.ProvisioningMessage{ RequestId: uuid.NewString(), MessageType: &azdext.ProvisioningMessage_StateRequest{ StateRequest: &azdext.ProvisioningStateRequest{ - Options: &azdext.ProvisioningStateOptions{}, + Options: &azdext.ProvisioningStateOptions{ + Hint: hint, + }, }, }, } @@ -124,6 +131,8 @@ func (p *ExternalProvisioningProvider) Deploy( }, } + // TODO: Route extension progress to CLI's interactive progress display. + // Currently progress is only logged; built-in providers surface through console formatter. resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { log.Printf("provisioning progress: %s", msg) }) @@ -152,6 +161,7 @@ func (p *ExternalProvisioningProvider) Preview( }, } + // TODO: Route to console progress display resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { log.Printf("provisioning preview progress: %s", msg) }) @@ -186,6 +196,7 @@ func (p *ExternalProvisioningProvider) Destroy( }, } + // TODO: Route to console progress display resp, err := p.broker.SendAndWaitWithProgress(ctx, req, func(msg string) { log.Printf("provisioning destroy progress: %s", msg) }) @@ -250,11 +261,37 @@ func (p *ExternalProvisioningProvider) Parameters( return convertFromProtoParameters(paramsResp.Parameters), nil } -// PlannedOutputs returns planned outputs. Not yet supported for external providers. +// PlannedOutputs returns planned outputs from the extension provider. func (p *ExternalProvisioningProvider) PlannedOutputs( ctx context.Context, ) ([]provisioning.PlannedOutput, error) { - return []provisioning.PlannedOutput{}, nil + req := &azdext.ProvisioningMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.ProvisioningMessage_PlannedOutputsRequest{ + PlannedOutputsRequest: &azdext.ProvisioningPlannedOutputsRequest{}, + }, + } + + resp, err := p.broker.SendAndWait(ctx, req) + if err != nil { + return nil, fmt.Errorf( + "provisioning planned outputs failed: %w", err, + ) + } + + outputsResp := resp.GetPlannedOutputsResponse() + if outputsResp == nil { + return []provisioning.PlannedOutput{}, nil + } + + result := make( + []provisioning.PlannedOutput, + len(outputsResp.PlannedOutputs), + ) + for i, o := range outputsResp.PlannedOutputs { + result[i] = provisioning.PlannedOutput{Name: o.Name} + } + return result, nil } // --- Conversion helpers --- @@ -275,6 +312,7 @@ func convertToProtoOptions( Module: options.Module, DeploymentStacks: deploymentStacks, IgnoreDeploymentState: options.IgnoreDeploymentState, + Name: options.Name, } // Convert Config to protobuf Struct if present @@ -372,6 +410,9 @@ func convertFromProtoPreviewResult( return &provisioning.DeployPreviewResult{} } + // Resource-level change details (created/modified/deleted) are not yet supported + // for extension providers. The proto can be extended with a repeated changes field + // when this capability is needed. preview := &provisioning.DeploymentPreview{ Status: result.Preview.Summary, Properties: &provisioning.DeploymentPreviewProperties{ diff --git a/cli/azd/internal/grpcserver/external_provisioning_provider_test.go b/cli/azd/internal/grpcserver/external_provisioning_provider_test.go new file mode 100644 index 00000000000..b5b90c7314e --- /dev/null +++ b/cli/azd/internal/grpcserver/external_provisioning_provider_test.go @@ -0,0 +1,363 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package grpcserver + +import ( + "testing" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_convertToProtoOptions(t *testing.T) { + tests := []struct { + name string + options provisioning.Options + verify func(t *testing.T, result *azdext.ProvisioningOptions, err error) + }{ + { + name: "FullOptions", + options: provisioning.Options{ + Provider: provisioning.Bicep, + Path: "/infra", + Module: "main", + Name: "layer1", + DeploymentStacks: map[string]any{ + "stackName": "my-stack", + "intVal": 42, + "boolVal": true, + }, + IgnoreDeploymentState: true, + Config: map[string]any{ + "key1": "value1", + "nested": map[string]any{ + "key2": "value2", + }, + }, + }, + verify: func(t *testing.T, result *azdext.ProvisioningOptions, err error) { + require.NoError(t, err) + require.NotNil(t, result) + assert.Equal(t, "bicep", result.Provider) + assert.Equal(t, "/infra", result.Path) + assert.Equal(t, "main", result.Module) + assert.Equal(t, "layer1", result.Name) + assert.True(t, result.IgnoreDeploymentState) + assert.Equal(t, "my-stack", result.DeploymentStacks["stackName"]) + assert.Equal(t, "42", result.DeploymentStacks["intVal"]) + assert.Equal(t, "true", result.DeploymentStacks["boolVal"]) + require.NotNil(t, result.Config) + assert.Equal(t, "value1", result.Config.Fields["key1"].GetStringValue()) + nested := result.Config.Fields["nested"].GetStructValue() + require.NotNil(t, nested) + assert.Equal(t, "value2", nested.Fields["key2"].GetStringValue()) + }, + }, + { + name: "EmptyOptions", + options: provisioning.Options{}, + verify: func(t *testing.T, result *azdext.ProvisioningOptions, err error) { + require.NoError(t, err) + require.NotNil(t, result) + assert.Empty(t, result.Provider) + assert.Empty(t, result.Path) + assert.Empty(t, result.Module) + assert.Empty(t, result.Name) + assert.False(t, result.IgnoreDeploymentState) + assert.Empty(t, result.DeploymentStacks) + assert.Nil(t, result.Config) + }, + }, + { + name: "NilConfig", + options: provisioning.Options{ + Provider: provisioning.Terraform, + Path: "/terraform", + }, + verify: func(t *testing.T, result *azdext.ProvisioningOptions, err error) { + require.NoError(t, err) + require.NotNil(t, result) + assert.Equal(t, "terraform", result.Provider) + assert.Nil(t, result.Config) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := convertToProtoOptions(tt.options) + tt.verify(t, result, err) + }) + } +} + +func Test_convertFromProtoStateResult(t *testing.T) { + tests := []struct { + name string + result *azdext.ProvisioningStateResult + verify func(t *testing.T, result *provisioning.StateResult) + }{ + { + name: "Nil", + result: nil, + verify: func(t *testing.T, result *provisioning.StateResult) { + require.NotNil(t, result) + assert.Nil(t, result.State) + }, + }, + { + name: "NilState", + result: &azdext.ProvisioningStateResult{State: nil}, + verify: func(t *testing.T, result *provisioning.StateResult) { + require.NotNil(t, result) + assert.Nil(t, result.State) + }, + }, + { + name: "EmptyState", + result: &azdext.ProvisioningStateResult{ + State: &azdext.ProvisioningState{}, + }, + verify: func(t *testing.T, result *provisioning.StateResult) { + require.NotNil(t, result) + require.NotNil(t, result.State) + assert.Empty(t, result.State.Outputs) + assert.Empty(t, result.State.Resources) + }, + }, + { + name: "PopulatedState", + result: &azdext.ProvisioningStateResult{ + State: &azdext.ProvisioningState{ + Outputs: map[string]*azdext.ProvisioningOutputParameter{ + "ENDPOINT": {Type: "string", Value: "https://example.com"}, + "PORT": {Type: "number", Value: "8080"}, + }, + Resources: []*azdext.ProvisioningResource{ + {Id: "/subscriptions/sub1/resourceGroups/rg1"}, + {Id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Web/sites/app1"}, + }, + }, + }, + verify: func(t *testing.T, result *provisioning.StateResult) { + require.NotNil(t, result) + require.NotNil(t, result.State) + assert.Len(t, result.State.Outputs, 2) + + endpoint := result.State.Outputs["ENDPOINT"] + assert.Equal(t, provisioning.ParameterType("string"), endpoint.Type) + assert.Equal(t, "https://example.com", endpoint.Value) + + port := result.State.Outputs["PORT"] + assert.Equal(t, provisioning.ParameterType("number"), port.Type) + assert.Equal(t, "8080", port.Value) + + assert.Len(t, result.State.Resources, 2) + assert.Equal(t, "/subscriptions/sub1/resourceGroups/rg1", result.State.Resources[0].Id) + assert.Equal( + t, + "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Web/sites/app1", + result.State.Resources[1].Id, + ) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convertFromProtoStateResult(tt.result) + tt.verify(t, result) + }) + } +} + +func Test_convertFromProtoDeployResult(t *testing.T) { + tests := []struct { + name string + result *azdext.ProvisioningDeployResult + verify func(t *testing.T, result *provisioning.DeployResult) + }{ + { + name: "EmptyResult", + result: &azdext.ProvisioningDeployResult{}, + verify: func(t *testing.T, result *provisioning.DeployResult) { + require.NotNil(t, result) + assert.Nil(t, result.Deployment) + assert.Empty(t, result.SkippedReason) + }, + }, + { + name: "FullDeployment", + result: &azdext.ProvisioningDeployResult{ + Deployment: &azdext.ProvisioningDeployment{ + Parameters: map[string]*azdext.ProvisioningInputParameter{ + "location": { + Type: "string", + DefaultValue: "eastus", + Value: "westus2", + }, + "sku": { + Type: "string", + Value: "B1", + }, + }, + Outputs: map[string]*azdext.ProvisioningOutputParameter{ + "ENDPOINT": {Type: "string", Value: "https://app.example.com"}, + }, + }, + }, + verify: func(t *testing.T, result *provisioning.DeployResult) { + require.NotNil(t, result) + require.NotNil(t, result.Deployment) + assert.Empty(t, result.SkippedReason) + + location := result.Deployment.Parameters["location"] + assert.Equal(t, "string", location.Type) + assert.Equal(t, "eastus", location.DefaultValue) + assert.Equal(t, "westus2", location.Value) + + sku := result.Deployment.Parameters["sku"] + assert.Equal(t, "string", sku.Type) + assert.Nil(t, sku.DefaultValue) + assert.Equal(t, "B1", sku.Value) + + endpoint := result.Deployment.Outputs["ENDPOINT"] + assert.Equal(t, provisioning.ParameterType("string"), endpoint.Type) + assert.Equal(t, "https://app.example.com", endpoint.Value) + }, + }, + { + name: "SkippedDeploymentState", + result: &azdext.ProvisioningDeployResult{ + SkippedReason: azdext.ProvisioningSkippedReason_PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE, + }, + verify: func(t *testing.T, result *provisioning.DeployResult) { + require.NotNil(t, result) + assert.Equal(t, provisioning.DeploymentStateSkipped, result.SkippedReason) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convertFromProtoDeployResult(tt.result) + tt.verify(t, result) + }) + } +} + +func Test_convertFromProtoPreviewResult(t *testing.T) { + tests := []struct { + name string + result *azdext.ProvisioningPreviewResult + verify func(t *testing.T, result *provisioning.DeployPreviewResult) + }{ + { + name: "Nil", + result: nil, + verify: func(t *testing.T, result *provisioning.DeployPreviewResult) { + require.NotNil(t, result) + assert.Nil(t, result.Preview) + }, + }, + { + name: "NilPreview", + result: &azdext.ProvisioningPreviewResult{Preview: nil}, + verify: func(t *testing.T, result *provisioning.DeployPreviewResult) { + require.NotNil(t, result) + assert.Nil(t, result.Preview) + }, + }, + { + name: "PopulatedPreview", + result: &azdext.ProvisioningPreviewResult{ + Preview: &azdext.ProvisioningDeploymentPreview{ + Summary: "3 resources to create, 1 to update", + }, + }, + verify: func(t *testing.T, result *provisioning.DeployPreviewResult) { + require.NotNil(t, result) + require.NotNil(t, result.Preview) + assert.Equal(t, "3 resources to create, 1 to update", result.Preview.Status) + require.NotNil(t, result.Preview.Properties) + assert.Empty(t, result.Preview.Properties.Changes) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convertFromProtoPreviewResult(tt.result) + tt.verify(t, result) + }) + } +} + +func Test_convertFromProtoParameters(t *testing.T) { + tests := []struct { + name string + params []*azdext.ProvisioningParameter + verify func(t *testing.T, result []provisioning.Parameter) + }{ + { + name: "Nil", + params: nil, + verify: func(t *testing.T, result []provisioning.Parameter) { + assert.Empty(t, result) + }, + }, + { + name: "EmptySlice", + params: []*azdext.ProvisioningParameter{}, + verify: func(t *testing.T, result []provisioning.Parameter) { + assert.Empty(t, result) + }, + }, + { + name: "MultipleParameters", + params: []*azdext.ProvisioningParameter{ + { + Name: "location", + Value: "eastus2", + }, + { + Name: "adminPassword", + Secret: true, + }, + { + Name: "appName", + Value: "my-app", + EnvVarMapping: []string{"AZURE_APP_NAME", "APP_NAME"}, + LocalPrompt: true, + UsingEnvVarMapping: true, + }, + }, + verify: func(t *testing.T, result []provisioning.Parameter) { + require.Len(t, result, 3) + + assert.Equal(t, "location", result[0].Name) + assert.Equal(t, "eastus2", result[0].Value) + assert.False(t, result[0].Secret) + + assert.Equal(t, "adminPassword", result[1].Name) + assert.True(t, result[1].Secret) + + assert.Equal(t, "appName", result[2].Name) + assert.Equal(t, "my-app", result[2].Value) + assert.Equal(t, []string{"AZURE_APP_NAME", "APP_NAME"}, result[2].EnvVarMapping) + assert.True(t, result[2].LocalPrompt) + assert.True(t, result[2].UsingEnvVarMapping) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convertFromProtoParameters(tt.params) + tt.verify(t, result) + }) + } +} diff --git a/cli/azd/pkg/azdext/models.pb.go b/cli/azd/pkg/azdext/models.pb.go index 33b0c307f7c..92e1a338420 100644 --- a/cli/azd/pkg/azdext/models.pb.go +++ b/cli/azd/pkg/azdext/models.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v7.34.1 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: models.proto package azdext diff --git a/cli/azd/pkg/azdext/provisioning.pb.go b/cli/azd/pkg/azdext/provisioning.pb.go index cb7c96ef9b7..2b98b5fc8e5 100644 --- a/cli/azd/pkg/azdext/provisioning.pb.go +++ b/cli/azd/pkg/azdext/provisioning.pb.go @@ -95,6 +95,8 @@ type ProvisioningMessage struct { // *ProvisioningMessage_ParametersRequest // *ProvisioningMessage_ParametersResponse // *ProvisioningMessage_ProgressMessage + // *ProvisioningMessage_PlannedOutputsRequest + // *ProvisioningMessage_PlannedOutputsResponse MessageType isProvisioningMessage_MessageType `protobuf_oneof:"message_type"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -304,6 +306,24 @@ func (x *ProvisioningMessage) GetProgressMessage() *ProvisioningProgressMessage return nil } +func (x *ProvisioningMessage) GetPlannedOutputsRequest() *ProvisioningPlannedOutputsRequest { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_PlannedOutputsRequest); ok { + return x.PlannedOutputsRequest + } + } + return nil +} + +func (x *ProvisioningMessage) GetPlannedOutputsResponse() *ProvisioningPlannedOutputsResponse { + if x != nil { + if x, ok := x.MessageType.(*ProvisioningMessage_PlannedOutputsResponse); ok { + return x.PlannedOutputsResponse + } + } + return nil +} + type isProvisioningMessage_MessageType interface { isProvisioningMessage_MessageType() } @@ -376,6 +396,14 @@ type ProvisioningMessage_ProgressMessage struct { ProgressMessage *ProvisioningProgressMessage `protobuf:"bytes,18,opt,name=progress_message,json=progressMessage,proto3,oneof"` } +type ProvisioningMessage_PlannedOutputsRequest struct { + PlannedOutputsRequest *ProvisioningPlannedOutputsRequest `protobuf:"bytes,19,opt,name=planned_outputs_request,json=plannedOutputsRequest,proto3,oneof"` +} + +type ProvisioningMessage_PlannedOutputsResponse struct { + PlannedOutputsResponse *ProvisioningPlannedOutputsResponse `protobuf:"bytes,20,opt,name=planned_outputs_response,json=plannedOutputsResponse,proto3,oneof"` +} + func (*ProvisioningMessage_RegisterProvisioningProviderRequest) isProvisioningMessage_MessageType() {} func (*ProvisioningMessage_RegisterProvisioningProviderResponse) isProvisioningMessage_MessageType() { @@ -411,6 +439,10 @@ func (*ProvisioningMessage_ParametersResponse) isProvisioningMessage_MessageType func (*ProvisioningMessage_ProgressMessage) isProvisioningMessage_MessageType() {} +func (*ProvisioningMessage_PlannedOutputsRequest) isProvisioningMessage_MessageType() {} + +func (*ProvisioningMessage_PlannedOutputsResponse) isProvisioningMessage_MessageType() {} + type RegisterProvisioningProviderRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -1075,6 +1107,7 @@ type ProvisioningOptions struct { DeploymentStacks map[string]string `protobuf:"bytes,4,rep,name=deployment_stacks,json=deploymentStacks,proto3" json:"deployment_stacks,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` IgnoreDeploymentState bool `protobuf:"varint,5,opt,name=ignore_deployment_state,json=ignoreDeploymentState,proto3" json:"ignore_deployment_state,omitempty"` Config *structpb.Struct `protobuf:"bytes,6,opt,name=config,proto3" json:"config,omitempty"` + Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` // Layer name for multi-layer deployments unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1151,8 +1184,16 @@ func (x *ProvisioningOptions) GetConfig() *structpb.Struct { return nil } +func (x *ProvisioningOptions) GetName() string { + if x != nil { + return x.Name + } + return "" +} + type ProvisioningStateOptions struct { state protoimpl.MessageState `protogen:"open.v1"` + Hint string `protobuf:"bytes,1,opt,name=hint,proto3" json:"hint,omitempty"` // Deployment name hint for state lookup unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1187,6 +1228,13 @@ func (*ProvisioningStateOptions) Descriptor() ([]byte, []int) { return file_provisioning_proto_rawDescGZIP(), []int{18} } +func (x *ProvisioningStateOptions) GetHint() string { + if x != nil { + return x.Hint + } + return "" +} + type ProvisioningStateResult struct { state protoimpl.MessageState `protogen:"open.v1"` State *ProvisioningState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` @@ -1887,11 +1935,135 @@ func (x *ProvisioningProgressMessage) GetTimestamp() int64 { return 0 } +type ProvisioningPlannedOutputsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPlannedOutputsRequest) Reset() { + *x = ProvisioningPlannedOutputsRequest{} + mi := &file_provisioning_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPlannedOutputsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPlannedOutputsRequest) ProtoMessage() {} + +func (x *ProvisioningPlannedOutputsRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPlannedOutputsRequest.ProtoReflect.Descriptor instead. +func (*ProvisioningPlannedOutputsRequest) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{32} +} + +type ProvisioningPlannedOutputsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + PlannedOutputs []*ProvisioningPlannedOutput `protobuf:"bytes,1,rep,name=planned_outputs,json=plannedOutputs,proto3" json:"planned_outputs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPlannedOutputsResponse) Reset() { + *x = ProvisioningPlannedOutputsResponse{} + mi := &file_provisioning_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPlannedOutputsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPlannedOutputsResponse) ProtoMessage() {} + +func (x *ProvisioningPlannedOutputsResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPlannedOutputsResponse.ProtoReflect.Descriptor instead. +func (*ProvisioningPlannedOutputsResponse) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{33} +} + +func (x *ProvisioningPlannedOutputsResponse) GetPlannedOutputs() []*ProvisioningPlannedOutput { + if x != nil { + return x.PlannedOutputs + } + return nil +} + +type ProvisioningPlannedOutput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProvisioningPlannedOutput) Reset() { + *x = ProvisioningPlannedOutput{} + mi := &file_provisioning_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProvisioningPlannedOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvisioningPlannedOutput) ProtoMessage() {} + +func (x *ProvisioningPlannedOutput) ProtoReflect() protoreflect.Message { + mi := &file_provisioning_proto_msgTypes[34] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvisioningPlannedOutput.ProtoReflect.Descriptor instead. +func (*ProvisioningPlannedOutput) Descriptor() ([]byte, []int) { + return file_provisioning_proto_rawDescGZIP(), []int{34} +} + +func (x *ProvisioningPlannedOutput) GetName() string { + if x != nil { + return x.Name + } + return "" +} + var File_provisioning_proto protoreflect.FileDescriptor const file_provisioning_proto_rawDesc = "" + "\n" + - "\x12provisioning.proto\x12\x06azdext\x1a$include/google/protobuf/struct.proto\x1a\ferrors.proto\"\xd8\f\n" + + "\x12provisioning.proto\x12\x06azdext\x1a$include/google/protobuf/struct.proto\x1a\ferrors.proto\"\xa5\x0e\n" + "\x13ProvisioningMessage\x12\x1d\n" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12,\n" + @@ -1913,7 +2085,9 @@ const file_provisioning_proto_rawDesc = "" + "\x13ensure_env_response\x18\x0f \x01(\v2%.azdext.ProvisioningEnsureEnvResponseH\x00R\x11ensureEnvResponse\x12V\n" + "\x12parameters_request\x18\x10 \x01(\v2%.azdext.ProvisioningParametersRequestH\x00R\x11parametersRequest\x12Y\n" + "\x13parameters_response\x18\x11 \x01(\v2&.azdext.ProvisioningParametersResponseH\x00R\x12parametersResponse\x12P\n" + - "\x10progress_message\x18\x12 \x01(\v2#.azdext.ProvisioningProgressMessageH\x00R\x0fprogressMessageB\x0e\n" + + "\x10progress_message\x18\x12 \x01(\v2#.azdext.ProvisioningProgressMessageH\x00R\x0fprogressMessage\x12c\n" + + "\x17planned_outputs_request\x18\x13 \x01(\v2).azdext.ProvisioningPlannedOutputsRequestH\x00R\x15plannedOutputsRequest\x12f\n" + + "\x18planned_outputs_response\x18\x14 \x01(\v2*.azdext.ProvisioningPlannedOutputsResponseH\x00R\x16plannedOutputsResponseB\x0e\n" + "\fmessage_type\"9\n" + "#RegisterProvisioningProviderRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"&\n" + @@ -1942,18 +2116,20 @@ const file_provisioning_proto_rawDesc = "" + "\x1eProvisioningParametersResponse\x12=\n" + "\n" + "parameters\x18\x01 \x03(\v2\x1d.azdext.ProvisioningParameterR\n" + - "parameters\"\xeb\x02\n" + + "parameters\"\xff\x02\n" + "\x13ProvisioningOptions\x12\x1a\n" + "\bprovider\x18\x01 \x01(\tR\bprovider\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x16\n" + "\x06module\x18\x03 \x01(\tR\x06module\x12^\n" + "\x11deployment_stacks\x18\x04 \x03(\v21.azdext.ProvisioningOptions.DeploymentStacksEntryR\x10deploymentStacks\x126\n" + "\x17ignore_deployment_state\x18\x05 \x01(\bR\x15ignoreDeploymentState\x12/\n" + - "\x06config\x18\x06 \x01(\v2\x17.google.protobuf.StructR\x06config\x1aC\n" + + "\x06config\x18\x06 \x01(\v2\x17.google.protobuf.StructR\x06config\x12\x12\n" + + "\x04name\x18\a \x01(\tR\x04name\x1aC\n" + "\x15DeploymentStacksEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x1a\n" + - "\x18ProvisioningStateOptions\"J\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\".\n" + + "\x18ProvisioningStateOptions\x12\x12\n" + + "\x04hint\x18\x01 \x01(\tR\x04hint\"J\n" + "\x17ProvisioningStateResult\x12/\n" + "\x05state\x18\x01 \x01(\v2\x19.azdext.ProvisioningStateR\x05state\"\xf2\x01\n" + "\x11ProvisioningState\x12@\n" + @@ -2017,7 +2193,12 @@ const file_provisioning_proto_rawDesc = "" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x18\n" + "\amessage\x18\x02 \x01(\tR\amessage\x12\x1c\n" + - "\ttimestamp\x18\x03 \x01(\x03R\ttimestamp*z\n" + + "\ttimestamp\x18\x03 \x01(\x03R\ttimestamp\"#\n" + + "!ProvisioningPlannedOutputsRequest\"p\n" + + "\"ProvisioningPlannedOutputsResponse\x12J\n" + + "\x0fplanned_outputs\x18\x01 \x03(\v2!.azdext.ProvisioningPlannedOutputR\x0eplannedOutputs\"/\n" + + "\x19ProvisioningPlannedOutput\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name*z\n" + "\x19ProvisioningSkippedReason\x12+\n" + "'PROVISIONING_SKIPPED_REASON_UNSPECIFIED\x10\x00\x120\n" + ",PROVISIONING_SKIPPED_REASON_DEPLOYMENT_STATE\x10\x012]\n" + @@ -2037,7 +2218,7 @@ func file_provisioning_proto_rawDescGZIP() []byte { } var file_provisioning_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_provisioning_proto_msgTypes = make([]protoimpl.MessageInfo, 38) +var file_provisioning_proto_msgTypes = make([]protoimpl.MessageInfo, 41) var file_provisioning_proto_goTypes = []any{ (ProvisioningSkippedReason)(0), // 0: azdext.ProvisioningSkippedReason (*ProvisioningMessage)(nil), // 1: azdext.ProvisioningMessage @@ -2072,17 +2253,20 @@ var file_provisioning_proto_goTypes = []any{ (*ProvisioningResource)(nil), // 30: azdext.ProvisioningResource (*ProvisioningParameter)(nil), // 31: azdext.ProvisioningParameter (*ProvisioningProgressMessage)(nil), // 32: azdext.ProvisioningProgressMessage - nil, // 33: azdext.ProvisioningOptions.DeploymentStacksEntry - nil, // 34: azdext.ProvisioningState.OutputsEntry - nil, // 35: azdext.ProvisioningDeployment.ParametersEntry - nil, // 36: azdext.ProvisioningDeployment.OutputsEntry - nil, // 37: azdext.ProvisioningDeploymentPreview.ParametersEntry - nil, // 38: azdext.ProvisioningDeploymentPreview.OutputsEntry - (*ExtensionError)(nil), // 39: azdext.ExtensionError - (*structpb.Struct)(nil), // 40: google.protobuf.Struct + (*ProvisioningPlannedOutputsRequest)(nil), // 33: azdext.ProvisioningPlannedOutputsRequest + (*ProvisioningPlannedOutputsResponse)(nil), // 34: azdext.ProvisioningPlannedOutputsResponse + (*ProvisioningPlannedOutput)(nil), // 35: azdext.ProvisioningPlannedOutput + nil, // 36: azdext.ProvisioningOptions.DeploymentStacksEntry + nil, // 37: azdext.ProvisioningState.OutputsEntry + nil, // 38: azdext.ProvisioningDeployment.ParametersEntry + nil, // 39: azdext.ProvisioningDeployment.OutputsEntry + nil, // 40: azdext.ProvisioningDeploymentPreview.ParametersEntry + nil, // 41: azdext.ProvisioningDeploymentPreview.OutputsEntry + (*ExtensionError)(nil), // 42: azdext.ExtensionError + (*structpb.Struct)(nil), // 43: google.protobuf.Struct } var file_provisioning_proto_depIdxs = []int32{ - 39, // 0: azdext.ProvisioningMessage.error:type_name -> azdext.ExtensionError + 42, // 0: azdext.ProvisioningMessage.error:type_name -> azdext.ExtensionError 2, // 1: azdext.ProvisioningMessage.register_provisioning_provider_request:type_name -> azdext.RegisterProvisioningProviderRequest 3, // 2: azdext.ProvisioningMessage.register_provisioning_provider_response:type_name -> azdext.RegisterProvisioningProviderResponse 4, // 3: azdext.ProvisioningMessage.initialize_request:type_name -> azdext.ProvisioningInitializeRequest @@ -2100,38 +2284,41 @@ var file_provisioning_proto_depIdxs = []int32{ 16, // 15: azdext.ProvisioningMessage.parameters_request:type_name -> azdext.ProvisioningParametersRequest 17, // 16: azdext.ProvisioningMessage.parameters_response:type_name -> azdext.ProvisioningParametersResponse 32, // 17: azdext.ProvisioningMessage.progress_message:type_name -> azdext.ProvisioningProgressMessage - 18, // 18: azdext.ProvisioningInitializeRequest.options:type_name -> azdext.ProvisioningOptions - 19, // 19: azdext.ProvisioningStateRequest.options:type_name -> azdext.ProvisioningStateOptions - 20, // 20: azdext.ProvisioningStateResponse.state_result:type_name -> azdext.ProvisioningStateResult - 23, // 21: azdext.ProvisioningDeployResponse.result:type_name -> azdext.ProvisioningDeployResult - 25, // 22: azdext.ProvisioningPreviewResponse.result:type_name -> azdext.ProvisioningPreviewResult - 26, // 23: azdext.ProvisioningDestroyRequest.options:type_name -> azdext.ProvisioningDestroyOptions - 27, // 24: azdext.ProvisioningDestroyResponse.result:type_name -> azdext.ProvisioningDestroyResult - 31, // 25: azdext.ProvisioningParametersResponse.parameters:type_name -> azdext.ProvisioningParameter - 33, // 26: azdext.ProvisioningOptions.deployment_stacks:type_name -> azdext.ProvisioningOptions.DeploymentStacksEntry - 40, // 27: azdext.ProvisioningOptions.config:type_name -> google.protobuf.Struct - 21, // 28: azdext.ProvisioningStateResult.state:type_name -> azdext.ProvisioningState - 34, // 29: azdext.ProvisioningState.outputs:type_name -> azdext.ProvisioningState.OutputsEntry - 30, // 30: azdext.ProvisioningState.resources:type_name -> azdext.ProvisioningResource - 35, // 31: azdext.ProvisioningDeployment.parameters:type_name -> azdext.ProvisioningDeployment.ParametersEntry - 36, // 32: azdext.ProvisioningDeployment.outputs:type_name -> azdext.ProvisioningDeployment.OutputsEntry - 22, // 33: azdext.ProvisioningDeployResult.deployment:type_name -> azdext.ProvisioningDeployment - 0, // 34: azdext.ProvisioningDeployResult.skipped_reason:type_name -> azdext.ProvisioningSkippedReason - 37, // 35: azdext.ProvisioningDeploymentPreview.parameters:type_name -> azdext.ProvisioningDeploymentPreview.ParametersEntry - 38, // 36: azdext.ProvisioningDeploymentPreview.outputs:type_name -> azdext.ProvisioningDeploymentPreview.OutputsEntry - 24, // 37: azdext.ProvisioningPreviewResult.preview:type_name -> azdext.ProvisioningDeploymentPreview - 29, // 38: azdext.ProvisioningState.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter - 28, // 39: azdext.ProvisioningDeployment.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter - 29, // 40: azdext.ProvisioningDeployment.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter - 28, // 41: azdext.ProvisioningDeploymentPreview.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter - 29, // 42: azdext.ProvisioningDeploymentPreview.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter - 1, // 43: azdext.ProvisioningService.Stream:input_type -> azdext.ProvisioningMessage - 1, // 44: azdext.ProvisioningService.Stream:output_type -> azdext.ProvisioningMessage - 44, // [44:45] is the sub-list for method output_type - 43, // [43:44] is the sub-list for method input_type - 43, // [43:43] is the sub-list for extension type_name - 43, // [43:43] is the sub-list for extension extendee - 0, // [0:43] is the sub-list for field type_name + 33, // 18: azdext.ProvisioningMessage.planned_outputs_request:type_name -> azdext.ProvisioningPlannedOutputsRequest + 34, // 19: azdext.ProvisioningMessage.planned_outputs_response:type_name -> azdext.ProvisioningPlannedOutputsResponse + 18, // 20: azdext.ProvisioningInitializeRequest.options:type_name -> azdext.ProvisioningOptions + 19, // 21: azdext.ProvisioningStateRequest.options:type_name -> azdext.ProvisioningStateOptions + 20, // 22: azdext.ProvisioningStateResponse.state_result:type_name -> azdext.ProvisioningStateResult + 23, // 23: azdext.ProvisioningDeployResponse.result:type_name -> azdext.ProvisioningDeployResult + 25, // 24: azdext.ProvisioningPreviewResponse.result:type_name -> azdext.ProvisioningPreviewResult + 26, // 25: azdext.ProvisioningDestroyRequest.options:type_name -> azdext.ProvisioningDestroyOptions + 27, // 26: azdext.ProvisioningDestroyResponse.result:type_name -> azdext.ProvisioningDestroyResult + 31, // 27: azdext.ProvisioningParametersResponse.parameters:type_name -> azdext.ProvisioningParameter + 36, // 28: azdext.ProvisioningOptions.deployment_stacks:type_name -> azdext.ProvisioningOptions.DeploymentStacksEntry + 43, // 29: azdext.ProvisioningOptions.config:type_name -> google.protobuf.Struct + 21, // 30: azdext.ProvisioningStateResult.state:type_name -> azdext.ProvisioningState + 37, // 31: azdext.ProvisioningState.outputs:type_name -> azdext.ProvisioningState.OutputsEntry + 30, // 32: azdext.ProvisioningState.resources:type_name -> azdext.ProvisioningResource + 38, // 33: azdext.ProvisioningDeployment.parameters:type_name -> azdext.ProvisioningDeployment.ParametersEntry + 39, // 34: azdext.ProvisioningDeployment.outputs:type_name -> azdext.ProvisioningDeployment.OutputsEntry + 22, // 35: azdext.ProvisioningDeployResult.deployment:type_name -> azdext.ProvisioningDeployment + 0, // 36: azdext.ProvisioningDeployResult.skipped_reason:type_name -> azdext.ProvisioningSkippedReason + 40, // 37: azdext.ProvisioningDeploymentPreview.parameters:type_name -> azdext.ProvisioningDeploymentPreview.ParametersEntry + 41, // 38: azdext.ProvisioningDeploymentPreview.outputs:type_name -> azdext.ProvisioningDeploymentPreview.OutputsEntry + 24, // 39: azdext.ProvisioningPreviewResult.preview:type_name -> azdext.ProvisioningDeploymentPreview + 35, // 40: azdext.ProvisioningPlannedOutputsResponse.planned_outputs:type_name -> azdext.ProvisioningPlannedOutput + 29, // 41: azdext.ProvisioningState.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 28, // 42: azdext.ProvisioningDeployment.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter + 29, // 43: azdext.ProvisioningDeployment.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 28, // 44: azdext.ProvisioningDeploymentPreview.ParametersEntry.value:type_name -> azdext.ProvisioningInputParameter + 29, // 45: azdext.ProvisioningDeploymentPreview.OutputsEntry.value:type_name -> azdext.ProvisioningOutputParameter + 1, // 46: azdext.ProvisioningService.Stream:input_type -> azdext.ProvisioningMessage + 1, // 47: azdext.ProvisioningService.Stream:output_type -> azdext.ProvisioningMessage + 47, // [47:48] is the sub-list for method output_type + 46, // [46:47] is the sub-list for method input_type + 46, // [46:46] is the sub-list for extension type_name + 46, // [46:46] is the sub-list for extension extendee + 0, // [0:46] is the sub-list for field type_name } func init() { file_provisioning_proto_init() } @@ -2158,6 +2345,8 @@ func file_provisioning_proto_init() { (*ProvisioningMessage_ParametersRequest)(nil), (*ProvisioningMessage_ParametersResponse)(nil), (*ProvisioningMessage_ProgressMessage)(nil), + (*ProvisioningMessage_PlannedOutputsRequest)(nil), + (*ProvisioningMessage_PlannedOutputsResponse)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -2165,7 +2354,7 @@ func file_provisioning_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_provisioning_proto_rawDesc), len(file_provisioning_proto_rawDesc)), NumEnums: 1, - NumMessages: 38, + NumMessages: 41, NumExtensions: 0, NumServices: 1, }, diff --git a/cli/azd/pkg/azdext/provisioning_envelope.go b/cli/azd/pkg/azdext/provisioning_envelope.go index 4b3d4a47418..66cdabdae79 100644 --- a/cli/azd/pkg/azdext/provisioning_envelope.go +++ b/cli/azd/pkg/azdext/provisioning_envelope.go @@ -79,6 +79,10 @@ func (ops *ProvisioningEnvelope) GetInnerMessage(msg *ProvisioningMessage) any { return m.ParametersRequest case *ProvisioningMessage_ParametersResponse: return m.ParametersResponse + case *ProvisioningMessage_PlannedOutputsRequest: + return m.PlannedOutputsRequest + case *ProvisioningMessage_PlannedOutputsResponse: + return m.PlannedOutputsResponse case *ProvisioningMessage_ProgressMessage: return m.ProgressMessage default: diff --git a/cli/azd/pkg/azdext/provisioning_manager.go b/cli/azd/pkg/azdext/provisioning_manager.go index dea43f3de2c..b2273c5bf13 100644 --- a/cli/azd/pkg/azdext/provisioning_manager.go +++ b/cli/azd/pkg/azdext/provisioning_manager.go @@ -27,6 +27,7 @@ type ProvisioningProvider interface { ) (*ProvisioningDestroyResult, error) EnsureEnv(ctx context.Context) error Parameters(ctx context.Context) ([]*ProvisioningParameter, error) + PlannedOutputs(ctx context.Context) ([]*ProvisioningPlannedOutput, error) } // ProvisioningProviderFactory describes a function that creates a provisioning provider instance. @@ -123,6 +124,9 @@ func (m *ProvisioningManager) ensureStream(ctx context.Context) error { if err := m.broker.On(m.onParameters); err != nil { return fmt.Errorf("failed to register parameters handler: %w", err) } + if err := m.broker.On(m.onPlannedOutputs); err != nil { + return fmt.Errorf("failed to register planned outputs handler: %w", err) + } return nil } @@ -377,3 +381,27 @@ func (m *ProvisioningManager) onParameters( }, }, nil } + +// onPlannedOutputs handles planned outputs requests +func (m *ProvisioningManager) onPlannedOutputs( + ctx context.Context, + req *ProvisioningPlannedOutputsRequest, +) (*ProvisioningMessage, error) { + provider, err := m.getProvider() + if err != nil { + return nil, err + } + + outputs, err := provider.PlannedOutputs(ctx) + if err != nil { + return nil, err + } + + return &ProvisioningMessage{ + MessageType: &ProvisioningMessage_PlannedOutputsResponse{ + PlannedOutputsResponse: &ProvisioningPlannedOutputsResponse{ + PlannedOutputs: outputs, + }, + }, + }, nil +} diff --git a/cli/azd/pkg/infra/provisioning/provisioning.go b/cli/azd/pkg/infra/provisioning/provisioning.go index c4f7e85deb3..d2bee5bcb33 100644 --- a/cli/azd/pkg/infra/provisioning/provisioning.go +++ b/cli/azd/pkg/infra/provisioning/provisioning.go @@ -50,8 +50,8 @@ func NewEnvRefreshResultFromState(state *State) contracts.EnvRefreshResult { return result } -// Parses the specified IaC Provider to ensure whether it is valid or not -// Defaults to `Bicep` if no provider is specified +// ParseProvider returns the specified IaC provider unchanged. +// Defaulting for NotSpecified is handled later during provider creation. func ParseProvider(kind ProviderKind) (ProviderKind, error) { return kind, nil } From bb8257c885f3b447ee668c4252cd0f93047b00c8 Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Fri, 3 Apr 2026 16:46:18 -0700 Subject: [PATCH 5/5] fix: Resolve cspell spelling check failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/azd/.vscode/cspell.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/azd/.vscode/cspell.yaml b/cli/azd/.vscode/cspell.yaml index ac54067021a..8e209de8167 100644 --- a/cli/azd/.vscode/cspell.yaml +++ b/cli/azd/.vscode/cspell.yaml @@ -367,6 +367,12 @@ overrides: words: - covdata - GOWORK + - filename: internal/grpcserver/external_provisioning_provider.go + words: + - CLI's + - filename: pkg/azdext/azd_client.go + words: + - MITM ignorePaths: - "**/*_test.go" - "**/mock*.go"