Skip to content

Commit

Permalink
Move EnsureEnv from prompt.Prompter to provisioning.Provider (#…
Browse files Browse the repository at this point in the history
…2465)

Different provisioning providers may require different values to be
set in the environment to be able to do a provision operation.  For
example, when using the bicep provider and doing a resource group
scoped deployment, in addition to a subscription and a location, we
need `AZURE_RESOURCE_GROUP` defined to name the target resource group to
deploy to.

This change prepares us to have providers require different values to
be set in the environment by moving `EnsureEnv` from `prompt.Prompter`
and to the providers themselves.

There should be no functional impact of this change, it is simply code
motion to help with some future changes.
  • Loading branch information
ellismg committed Jun 30, 2023
1 parent e59a77c commit 38a93b9
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 61 deletions.
30 changes: 19 additions & 11 deletions cli/azd/cmd/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import (
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
"github.com/azure/azure-dev/cli/azd/pkg/pipeline"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -108,11 +110,13 @@ func newPipelineConfigCmd() *cobra.Command {

// pipelineConfigAction defines the action for pipeline config command
type pipelineConfigAction struct {
flags *pipelineConfigFlags
manager *pipeline.PipelineManager
env *environment.Environment
console input.Console
prompters prompt.Prompter
flags *pipelineConfigFlags
manager *pipeline.PipelineManager
provisioningManager *provisioning.Manager
env *environment.Environment
console input.Console
prompters prompt.Prompter
projectConfig *project.ProjectConfig
}

func newPipelineConfigAction(
Expand All @@ -122,21 +126,25 @@ func newPipelineConfigAction(
flags *pipelineConfigFlags,
prompters prompt.Prompter,
manager *pipeline.PipelineManager,
provisioningManager *provisioning.Manager,
projectConfig *project.ProjectConfig,
) actions.Action {
pca := &pipelineConfigAction{
flags: flags,
manager: manager,
env: env,
console: console,
prompters: prompters,
flags: flags,
manager: manager,
env: env,
console: console,
prompters: prompters,
provisioningManager: provisioningManager,
projectConfig: projectConfig,
}

return pca
}

// Run implements action interface
func (p *pipelineConfigAction) Run(ctx context.Context) (*actions.ActionResult, error) {
err := p.prompters.EnsureEnv(ctx)
err := p.provisioningManager.Initialize(ctx, p.projectConfig.Path, p.projectConfig.Infra)
if err != nil {
return nil, err
}
Expand Down
6 changes: 5 additions & 1 deletion cli/azd/cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
Expand Down Expand Up @@ -60,6 +61,7 @@ type upAction struct {
console input.Console
runner middleware.MiddlewareContext
prompters prompt.Prompter
provisioningManager *provisioning.Manager
}

func newUpAction(
Expand All @@ -73,6 +75,7 @@ func newUpAction(
console input.Console,
runner middleware.MiddlewareContext,
prompters prompt.Prompter,
provisioningManager *provisioning.Manager,
) actions.Action {
return &upAction{
flags: flags,
Expand All @@ -84,6 +87,7 @@ func newUpAction(
console: console,
runner: runner,
prompters: prompters,
provisioningManager: provisioningManager,
}
}

Expand All @@ -107,7 +111,7 @@ func (u *upAction) Run(ctx context.Context) (*actions.ActionResult, error) {
output.WithWarningFormat("WARNING: The '--service' flag is deprecated and will be removed in a future release."))
}

err := u.prompters.EnsureEnv(ctx)
err := u.provisioningManager.Initialize(ctx, u.projectConfig.Path, u.projectConfig.Infra)
if err != nil {
return nil, err
}
Expand Down
11 changes: 10 additions & 1 deletion cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@ func (p *BicepProvider) Initialize(ctx context.Context, projectPath string, opti
return err
}

return p.prompters.EnsureEnv(ctx)
return p.EnsureEnv(ctx)
}

// EnsureEnv ensures that the environment is in a provision-ready state with required values set, prompting the user if
// values are unset.
//
// An environment is considered to be in a provision-ready state if it contains both an AZURE_SUBSCRIPTION_ID and
// AZURE_LOCATION value.
func (p *BicepProvider) EnsureEnv(ctx context.Context) error {
return EnsureSubscriptionAndLocation(ctx, p.env, p.prompters)
}

func (p *BicepProvider) State(ctx context.Context) (*StateResult, error) {
Expand Down
42 changes: 42 additions & 0 deletions cli/azd/pkg/infra/provisioning/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ import (
"context"
"fmt"

"github.com/azure/azure-dev/cli/azd/pkg/account"
"github.com/azure/azure-dev/cli/azd/pkg/alpha"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/ioc"
"github.com/azure/azure-dev/cli/azd/pkg/prompt"
)

// Manages the orchestration of infrastructure provisioning
type Manager struct {
serviceLocator ioc.ServiceLocator
env *environment.Environment
console input.Console
prompter prompt.Prompter
provider Provider
alphaFeatureManager *alpha.FeatureManager
projectPath string
Expand Down Expand Up @@ -96,18 +99,57 @@ func (m *Manager) Destroy(ctx context.Context, options DestroyOptions) (*Destroy
return destroyResult, nil
}

// EnsureSubscriptionAndLocation ensures that that that subscription (AZURE_SUBSCRIPTION_ID) and location (AZURE_LOCATION)
// variables are set in the environment, prompting the user for the values if they do not exist.
func EnsureSubscriptionAndLocation(ctx context.Context, env *environment.Environment, prompter prompt.Prompter) error {
if env.GetSubscriptionId() == "" {
subscriptionId, err := prompter.PromptSubscription(ctx, "Select an Azure Subscription to use:")
if err != nil {
return err
}

env.SetSubscriptionId(subscriptionId)

if err := env.Save(); err != nil {
return err
}
}

if env.GetLocation() == "" {
location, err := prompter.PromptLocation(
ctx,
env.GetSubscriptionId(),
"Select an Azure location to use:",
func(_ account.Location) bool { return true },
)
if err != nil {
return err
}

env.SetLocation(location)

if err := env.Save(); err != nil {
return err
}
}

return nil
}

// Creates a new instance of the Provisioning Manager
func NewManager(
serviceLocator ioc.ServiceLocator,
env *environment.Environment,
console input.Console,
alphaFeatureManager *alpha.FeatureManager,
prompter prompt.Prompter,
) *Manager {
return &Manager{
serviceLocator: serviceLocator,
env: env,
console: console,
alphaFeatureManager: alphaFeatureManager,
prompter: prompter,
}
}

Expand Down
12 changes: 6 additions & 6 deletions cli/azd/pkg/infra/provisioning/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestProvisionInitializesEnvironment(t *testing.T) {

registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand All @@ -59,7 +59,7 @@ func TestManagerPlan(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand All @@ -79,7 +79,7 @@ func TestManagerGetState(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand All @@ -98,7 +98,7 @@ func TestManagerDeploy(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand All @@ -122,7 +122,7 @@ func TestManagerDestroyWithPositiveConfirmation(t *testing.T) {

registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand All @@ -148,7 +148,7 @@ func TestManagerDestroyWithNegativeConfirmation(t *testing.T) {

registerContainerDependencies(mockContext, env)

mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager)
mgr := NewManager(mockContext.Container, env, mockContext.Console, mockContext.AlphaFeaturesManager, nil)
err := mgr.Initialize(*mockContext.Context, "", Options{Provider: "test"})
require.NoError(t, err)

Expand Down
1 change: 1 addition & 0 deletions cli/azd/pkg/infra/provisioning/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ type Provider interface {
Plan(ctx context.Context) (*DeploymentPlan, error)
Deploy(ctx context.Context, plan *DeploymentPlan) (*DeployResult, error)
Destroy(ctx context.Context, options DestroyOptions) (*DestroyResult, error)
EnsureEnv(ctx context.Context) error
}
11 changes: 10 additions & 1 deletion cli/azd/pkg/infra/provisioning/terraform/terraform_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (t *TerraformProvider) Initialize(ctx context.Context, projectPath string,
return err
}

if err := t.prompters.EnsureEnv(ctx); err != nil {
if err := t.EnsureEnv(ctx); err != nil {
return err
}

Expand All @@ -105,6 +105,15 @@ func (t *TerraformProvider) Initialize(ctx context.Context, projectPath string,
return nil
}

// EnsureEnv ensures that the environment is in a provision-ready state with required values set, prompting the user if
// values are unset.
//
// An environment is considered to be in a provision-ready state if it contains both an AZURE_SUBSCRIPTION_ID and
// AZURE_LOCATION value.
func (t *TerraformProvider) EnsureEnv(ctx context.Context) error {
return EnsureSubscriptionAndLocation(ctx, t.env, t.prompters)
}

// Previews the infrastructure through terraform plan
func (t *TerraformProvider) Plan(ctx context.Context) (*DeploymentPlan, error) {
isRemoteBackendConfig, err := t.isRemoteBackendConfig()
Expand Down
11 changes: 10 additions & 1 deletion cli/azd/pkg/infra/provisioning/test/test_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ func (p *TestProvider) Initialize(ctx context.Context, projectPath string, optio
p.projectPath = projectPath
p.options = options

return p.prompters.EnsureEnv(ctx)
return p.EnsureEnv(ctx)
}

// EnsureEnv ensures that the environment is in a provision-ready state with required values set, prompting the user if
// values are unset.
//
// An environment is considered to be in a provision-ready state if it contains both an AZURE_SUBSCRIPTION_ID and
// AZURE_LOCATION value.
func (t *TestProvider) EnsureEnv(ctx context.Context) error {
return EnsureSubscriptionAndLocation(ctx, t.env, t.prompters)
}

func (p *TestProvider) Plan(ctx context.Context) (*DeploymentPlan, error) {
Expand Down
40 changes: 0 additions & 40 deletions cli/azd/pkg/prompt/prompter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
type LocationFilterPredicate func(loc account.Location) bool

type Prompter interface {
EnsureEnv(ctx context.Context) error
PromptSubscription(ctx context.Context, msg string) (subscriptionId string, err error)
PromptLocation(ctx context.Context, subId string, msg string, filter LocationFilterPredicate) (string, error)
PromptResourceGroup(ctx context.Context) (string, error)
Expand All @@ -48,45 +47,6 @@ func NewDefaultPrompter(
}
}

// EnsureEnv ensures that the environment is in a provision-ready state with required values set, prompting the user if
// values are unset.
//
// This currently means that subscription (AZURE_SUBSCRIPTION_ID) and location (AZURE_LOCATION) variables are set.
func (p *DefaultPrompter) EnsureEnv(ctx context.Context) error {
if p.env.GetSubscriptionId() == "" {
subscriptionId, err := p.PromptSubscription(ctx, "Select an Azure Subscription to use:")
if err != nil {
return err
}

p.env.SetSubscriptionId(subscriptionId)

if err := p.env.Save(); err != nil {
return err
}
}

if p.env.GetLocation() == "" {
location, err := p.PromptLocation(
ctx,
p.env.GetSubscriptionId(),
"Select an Azure location to use:",
func(_ account.Location) bool { return true },
)
if err != nil {
return err
}

p.env.SetLocation(location)

if err := p.env.Save(); err != nil {
return err
}
}

return nil
}

func (p *DefaultPrompter) PromptSubscription(ctx context.Context, msg string) (subscriptionId string, err error) {
subscriptionOptions, defaultSubscription, err := p.getSubscriptionOptions(ctx)
if err != nil {
Expand Down

0 comments on commit 38a93b9

Please sign in to comment.