Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/azd/internal/grpcserver/deployment_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (s *deploymentService) GetDeployment(
return nil, err
}

if err := bicepProvider.Initialize(ctx, azdContext.ProjectDirectory(), projectConfig.Infra); err != nil {
if err := bicepProvider.Initialize(ctx, azdContext.ProjectDirectory(), projectConfig.Infra.ToProvisioningOptions()); err != nil {
return nil, err
}

Expand Down
12 changes: 8 additions & 4 deletions cli/azd/pkg/project/dotnet_importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ func (ai *DotNetImporter) Services(
svc.Project = p
svc.EventDispatcher = ext.NewEventDispatcher[ServiceLifecycleEventArgs]()

svc.Infra.Provider, err = provisioning.ParseProvider(svc.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(svc.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}
svc.Infra.Provider = string(parsedProvider)

// handle container files
containerFiles := manifest.Resources[name].ContainerFiles
Expand Down Expand Up @@ -281,10 +282,11 @@ func (ai *DotNetImporter) Services(
svc.Project = p
svc.EventDispatcher = ext.NewEventDispatcher[ServiceLifecycleEventArgs]()

svc.Infra.Provider, err = provisioning.ParseProvider(svc.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(svc.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}
svc.Infra.Provider = string(parsedProvider)

svc.DotNetContainerApp = &DotNetContainerAppOptions{
Manifest: manifest,
Expand All @@ -308,10 +310,11 @@ func (ai *DotNetImporter) Services(
svc.Project = p
svc.EventDispatcher = ext.NewEventDispatcher[ServiceLifecycleEventArgs]()

svc.Infra.Provider, err = provisioning.ParseProvider(svc.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(svc.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}
svc.Infra.Provider = string(parsedProvider)

svc.DotNetContainerApp = &DotNetContainerAppOptions{
ContainerImage: container.Image,
Expand Down Expand Up @@ -396,10 +399,11 @@ func (ai *DotNetImporter) Services(
svc.Project = p
svc.EventDispatcher = ext.NewEventDispatcher[ServiceLifecycleEventArgs]()

svc.Infra.Provider, err = provisioning.ParseProvider(svc.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(svc.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}
svc.Infra.Provider = string(parsedProvider)

// handle container files
containerFiles := manifest.Resources[name].ContainerFiles
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/pkg/project/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ var (
// are not explicitly defined, the project importer uses default values to find the infrastructure.
func (im *ImportManager) ProjectInfrastructure(ctx context.Context, projectConfig *ProjectConfig) (*Infra, error) {
mergedOptions := provisioning.Options{}
mergo.Merge(&mergedOptions, projectConfig.Infra)
mergo.Merge(&mergedOptions, projectConfig.Infra.ToProvisioningOptions())
mergo.Merge(&mergedOptions, DefaultProvisioningOptions)

infraRoot := mergedOptions.Path
Expand Down
4 changes: 2 additions & 2 deletions cli/azd/pkg/project/importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,10 @@ func TestImportManagerProjectInfrastructure(t *testing.T) {
defer os.Remove(path)

r, e := manager.ProjectInfrastructure(*mockContext.Context, &ProjectConfig{
Infra: provisioning.Options{
Infra: InfraConfigFromProvisioningOptions(provisioning.Options{
Path: expectedDefaultFolder,
Module: expectedDefaultModule,
},
}),
})

require.NoError(t, e)
Expand Down
51 changes: 51 additions & 0 deletions cli/azd/pkg/project/infra_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package project

import "github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"

// InfraConfig represents infrastructure configuration from azure.yaml
// This is a data-only representation that can be converted to provisioning.Options
type InfraConfig struct {
Provider string `yaml:"provider,omitempty"`
Path string `yaml:"path,omitempty"`
Module string `yaml:"module,omitempty"`
Name string `yaml:"name,omitempty"`
Layers []InfraConfig `yaml:"layers,omitempty"`
DeploymentStacks map[string]any `yaml:"deploymentStacks,omitempty"`
}

// ToProvisioningOptions converts InfraConfig to provisioning.Options
func (ic *InfraConfig) ToProvisioningOptions() provisioning.Options {
layers := make([]provisioning.Options, len(ic.Layers))
for i, layer := range ic.Layers {
layers[i] = layer.ToProvisioningOptions()
}

return provisioning.Options{
Provider: provisioning.ProviderKind(ic.Provider),
Path: ic.Path,
Module: ic.Module,
Name: ic.Name,
DeploymentStacks: ic.DeploymentStacks,
Layers: layers,
}
}

// InfraConfigFromProvisioningOptions creates InfraConfig from provisioning.Options
func InfraConfigFromProvisioningOptions(opts provisioning.Options) InfraConfig {
layers := make([]InfraConfig, len(opts.Layers))
for i, layer := range opts.Layers {
layers[i] = InfraConfigFromProvisioningOptions(layer)
}

return InfraConfig{
Provider: string(opts.Provider),
Path: opts.Path,
Module: opts.Module,
Name: opts.Name,
DeploymentStacks: opts.DeploymentStacks,
Layers: layers,
}
}
125 changes: 125 additions & 0 deletions cli/azd/pkg/project/infra_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package project

import (
"testing"

"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestInfraConfig_ToProvisioningOptions(t *testing.T) {
infraConfig := InfraConfig{
Provider: "terraform",
Path: "custom-infra",
Module: "custom-module",
Name: "test-infra",
Layers: []InfraConfig{
{
Name: "layer1",
Provider: "bicep",
Path: "layer1-path",
Module: "layer1-module",
},
},
DeploymentStacks: map[string]any{
"key": "value",
},
}

provOpts := infraConfig.ToProvisioningOptions()

assert.Equal(t, provisioning.ProviderKind("terraform"), provOpts.Provider)
assert.Equal(t, "custom-infra", provOpts.Path)
assert.Equal(t, "custom-module", provOpts.Module)
assert.Equal(t, "test-infra", provOpts.Name)
assert.Equal(t, map[string]any{"key": "value"}, provOpts.DeploymentStacks)

require.Len(t, provOpts.Layers, 1)
assert.Equal(t, "layer1", provOpts.Layers[0].Name)
assert.Equal(t, provisioning.ProviderKind("bicep"), provOpts.Layers[0].Provider)
assert.Equal(t, "layer1-path", provOpts.Layers[0].Path)
assert.Equal(t, "layer1-module", provOpts.Layers[0].Module)
}

func TestInfraConfigFromProvisioningOptions(t *testing.T) {
provOpts := provisioning.Options{
Provider: provisioning.Terraform,
Path: "custom-infra",
Module: "custom-module",
Name: "test-infra",
Layers: []provisioning.Options{
{
Name: "layer1",
Provider: provisioning.Bicep,
Path: "layer1-path",
Module: "layer1-module",
},
},
DeploymentStacks: map[string]any{
"key": "value",
},
}

infraConfig := InfraConfigFromProvisioningOptions(provOpts)

assert.Equal(t, "terraform", infraConfig.Provider)
assert.Equal(t, "custom-infra", infraConfig.Path)
assert.Equal(t, "custom-module", infraConfig.Module)
assert.Equal(t, "test-infra", infraConfig.Name)
assert.Equal(t, map[string]any{"key": "value"}, infraConfig.DeploymentStacks)

require.Len(t, infraConfig.Layers, 1)
assert.Equal(t, "layer1", infraConfig.Layers[0].Name)
assert.Equal(t, "bicep", infraConfig.Layers[0].Provider)
assert.Equal(t, "layer1-path", infraConfig.Layers[0].Path)
assert.Equal(t, "layer1-module", infraConfig.Layers[0].Module)
}

func TestInfraConfig_RoundTrip(t *testing.T) {
original := provisioning.Options{
Provider: provisioning.Terraform,
Path: "infra",
Module: "main",
Name: "my-infra",
Layers: []provisioning.Options{
{
Name: "networking",
Provider: provisioning.Bicep,
Path: "infra/network",
Module: "network",
},
{
Name: "application",
Provider: provisioning.Terraform,
Path: "infra/app",
Module: "app",
},
},
DeploymentStacks: map[string]any{
"enabled": true,
},
}

// Convert to InfraConfig and back
infraConfig := InfraConfigFromProvisioningOptions(original)
result := infraConfig.ToProvisioningOptions()

// Verify round-trip conversion
assert.Equal(t, original.Provider, result.Provider)
assert.Equal(t, original.Path, result.Path)
assert.Equal(t, original.Module, result.Module)
assert.Equal(t, original.Name, result.Name)
assert.Equal(t, original.DeploymentStacks, result.DeploymentStacks)

require.Len(t, result.Layers, 2)
for i := range original.Layers {
assert.Equal(t, original.Layers[i].Name, result.Layers[i].Name)
assert.Equal(t, original.Layers[i].Provider, result.Layers[i].Provider)
assert.Equal(t, original.Layers[i].Path, result.Layers[i].Path)
assert.Equal(t, original.Layers[i].Module, result.Layers[i].Module)
}
}
5 changes: 2 additions & 3 deletions cli/azd/pkg/project/mapper_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/azure/azure-dev/cli/azd/internal/mapper"
"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/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"google.golang.org/protobuf/types/known/structpb"
)
Expand Down Expand Up @@ -708,8 +707,8 @@ func registerProjectMappings() {

// Convert infra options if present
if src.Infra != nil {
result.Infra = provisioning.Options{
Provider: provisioning.ProviderKind(src.Infra.Provider),
result.Infra = InfraConfig{
Provider: src.Infra.Provider,
Path: src.Infra.Path,
Module: src.Infra.Module,
}
Expand Down
15 changes: 10 additions & 5 deletions cli/azd/pkg/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,22 @@ func Parse(ctx context.Context, yamlContent string) (*ProjectConfig, error) {
}
}

if err := projectConfig.Infra.Validate(); err != nil {
provOpts := projectConfig.Infra.ToProvisioningOptions()
if err := provOpts.Validate(); err != nil {
return nil, err
}

var err error
projectConfig.Infra.Provider, err = provisioning.ParseProvider(projectConfig.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(projectConfig.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing project %s: %w", projectConfig.Name, err)
}
projectConfig.Infra.Provider = string(parsedProvider)

for _, layer := range projectConfig.Infra.Layers {
layer.Provider = projectConfig.Infra.Provider
for i := range projectConfig.Infra.Layers {
if projectConfig.Infra.Layers[i].Provider == "" {
projectConfig.Infra.Layers[i].Provider = projectConfig.Infra.Provider
}
}

if strings.Contains(projectConfig.Infra.Path, "\\") && !strings.Contains(projectConfig.Infra.Path, "/") {
Expand All @@ -107,10 +111,11 @@ func Parse(ctx context.Context, yamlContent string) (*ProjectConfig, error) {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}

svc.Infra.Provider, err = provisioning.ParseProvider(svc.Infra.Provider)
parsedProvider, err := provisioning.ParseProvider(provisioning.ProviderKind(svc.Infra.Provider))
if err != nil {
return nil, fmt.Errorf("parsing service %s: %w", svc.Name, err)
}
svc.Infra.Provider = string(parsedProvider)

if strings.Contains(svc.Infra.Path, "\\") && !strings.Contains(svc.Infra.Path, "/") {
svc.Infra.Path = strings.ReplaceAll(svc.Infra.Path, "\\", "/")
Expand Down
3 changes: 1 addition & 2 deletions cli/azd/pkg/project/project_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/ext"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/pkg/platform"
"github.com/azure/azure-dev/cli/azd/pkg/state"
Expand All @@ -32,7 +31,7 @@ type ProjectConfig struct {
Path string `yaml:"-"`
Metadata *ProjectMetadata `yaml:"metadata,omitempty"`
Services map[string]*ServiceConfig `yaml:"services,omitempty"`
Infra provisioning.Options `yaml:"infra,omitempty"`
Infra InfraConfig `yaml:"infra,omitempty"`
Pipeline PipelineOptions `yaml:"pipeline,omitempty"`
Hooks HooksConfig `yaml:"hooks,omitempty"`
State *state.Config `yaml:"state,omitempty"`
Expand Down
Loading