Skip to content

Commit

Permalink
compose: enable synth-infra and temp-infra
Browse files Browse the repository at this point in the history
  • Loading branch information
weikanglim committed Oct 23, 2024
1 parent 7080ac5 commit e2b12da
Show file tree
Hide file tree
Showing 9 changed files with 424 additions and 290 deletions.
1 change: 1 addition & 0 deletions cli/azd/.vscode/cspell-azd-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ overriden
paketobuildpacks
patternmatcher
pflag
pgadmin
posix
preinit
proxying
Expand Down
76 changes: 76 additions & 0 deletions cli/azd/internal/scaffold/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/resources"
"github.com/psanford/memfs"
)

const baseRoot = "scaffold/base"
Expand Down Expand Up @@ -133,6 +134,81 @@ func ExecInfra(
return nil
}

// ExecInfra scaffolds infrastructure files for the given spec, using the loaded templates in t. The resulting files
// are written to the target directory.
func ExecInfraFs(
t *template.Template,
spec InfraSpec) (*memfs.FS, error) {
fs := memfs.New()

// Pre-execution expansion. Additional parameters are added, derived from the initial spec.
preExecExpand(&spec)

err := copyFsToMemFs(resources.ScaffoldBase, fs, baseRoot, ".")
if err != nil {
return nil, err
}

err = executeToFS(fs, t, "main.bicep", "main.bicep", spec)
if err != nil {
return nil, fmt.Errorf("scaffolding main.bicep: %w", err)
}

err = executeToFS(fs, t, "resources.bicep", "resources.bicep", spec)
if err != nil {
return nil, fmt.Errorf("scaffolding resources.bicep: %w", err)
}

err = executeToFS(fs, t, "main.parameters.json", "main.parameters.json", spec)
if err != nil {
return nil, fmt.Errorf("scaffolding main.parameters.json: %w", err)
}

return fs, nil
}

func copyFsToMemFs(embedFs fs.FS, targetFs *memfs.FS, root string, target string) error {
return fs.WalkDir(embedFs, root, func(name string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
targetPath := name[len(root):]
if target != "" {
targetPath = path.Join(target, name[len(root):])
}

if d.IsDir() {
return targetFs.MkdirAll(targetPath, osutil.PermissionDirectory)
}

contents, err := fs.ReadFile(embedFs, name)
if err != nil {
return fmt.Errorf("reading file: %w", err)
}
return targetFs.WriteFile(targetPath, contents, osutil.PermissionFile)
})
}

// executeToFS executes the given template with the given name and context, and writes the result to the given path in
// the given target filesystem.
func executeToFS(targetFS *memfs.FS, tmpl *template.Template, name string, path string, context any) error {
buf := bytes.NewBufferString("")

if err := tmpl.ExecuteTemplate(buf, name, context); err != nil {
return fmt.Errorf("executing template: %w", err)
}

if err := targetFS.MkdirAll(filepath.Dir(path), osutil.PermissionDirectory); err != nil {
return fmt.Errorf("creating directory: %w", err)
}

if err := targetFS.WriteFile(path, buf.Bytes(), osutil.PermissionFile); err != nil {
return fmt.Errorf("writing file: %w", err)
}

return nil
}

func preExecExpand(spec *InfraSpec) {
// postgres requires specific password seeding parameters
if spec.DbPostgres != nil {
Expand Down
1 change: 1 addition & 0 deletions cli/azd/pkg/alpha/alpha_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package alpha

const (
TerraformId FeatureId = "terraform"
Compose FeatureId = "compose"
)
2 changes: 1 addition & 1 deletion cli/azd/pkg/pipeline/pipeline_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ func createPipelineManager(
mockContext.Console,
args,
mockContext.Container,
project.NewImportManager(nil),
project.NewImportManager(project.NewDotNetImporter(nil, nil, nil, nil, mockContext.AlphaFeaturesManager)),
&mockUserConfigManager{},
)
}
Expand Down
11 changes: 9 additions & 2 deletions cli/azd/pkg/project/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"slices"
"strings"

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

Expand Down Expand Up @@ -162,8 +163,8 @@ func (im *ImportManager) ProjectInfrastructure(ctx context.Context, projectConfi
}
}

if len(projectConfig.Resources) > 0 {
return tempInfra(ctx, im.dotNetImporter.alphaFeatureManager, projectConfig)
if im.dotNetImporter.alphaFeatureManager.IsEnabled(alpha.FeatureId(alpha.Compose)) && len(projectConfig.Resources) > 0 {
return tempInfra(ctx, projectConfig)
}

return &Infra{}, nil
Expand All @@ -184,6 +185,8 @@ func pathHasModule(path, module string) (bool, error) {

}

// SynthAllInfrastructure returns a file system containing all infrastructure for the project,
// rooted at the project directory.
func (im *ImportManager) SynthAllInfrastructure(ctx context.Context, projectConfig *ProjectConfig) (fs.FS, error) {
for _, svcConfig := range projectConfig.Services {
if svcConfig.Language == ServiceLanguageDotNet {
Expand All @@ -195,6 +198,10 @@ func (im *ImportManager) SynthAllInfrastructure(ctx context.Context, projectConf
}
}

if im.dotNetImporter.alphaFeatureManager.IsEnabled(alpha.FeatureId(alpha.Compose)) && len(projectConfig.Resources) > 0 {
return infraFsForProject(ctx, projectConfig)
}

return nil, fmt.Errorf("this project does not contain any infrastructure to synthesize")
}

Expand Down
94 changes: 93 additions & 1 deletion cli/azd/pkg/project/importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet"
"github.com/azure/azure-dev/cli/azd/test/mocks"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockenv"
"github.com/braydonk/yaml"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -181,7 +183,8 @@ func TestImportManagerProjectInfrastructureDefaults(t *testing.T) {
lazyEnvManager: lazy.NewLazy(func() (environment.Manager, error) {
return mockEnv, nil
}),
hostCheck: make(map[string]hostCheckResult),
hostCheck: make(map[string]hostCheckResult),
alphaFeatureManager: mockContext.AlphaFeaturesManager,
})

// Get defaults and error b/c no infra found and no Aspire project
Expand Down Expand Up @@ -371,3 +374,92 @@ func TestImportManagerProjectInfrastructureAspire(t *testing.T) {
require.NoError(t, e)
require.Equal(t, 1, manifestInvokeCount)
}

const prjWithResources = `
name: myproject
resources:
api:
type: host.containerapp
port: 80
uses:
- postgresdb
- mongodb
- redis
web:
type: host.containerapp
port: 80
uses:
- api
postgresdb:
type: db.postgres
mongodb:
type: db.mongo
redis:
type: db.redis
`

func Test_ImportManager_ProjectInfrastructure_FromResources(t *testing.T) {
alpha.SetDefaultEnablement(string(alpha.Compose), true)
t.Cleanup(func() { alpha.SetDefaultEnablement(string(alpha.Compose), false) })

im := &ImportManager{
dotNetImporter: &DotNetImporter{
alphaFeatureManager: alpha.NewFeaturesManagerWithConfig(config.NewEmptyConfig()),
},
}

prjConfig := &ProjectConfig{}
err := yaml.Unmarshal([]byte(prjWithResources), prjConfig)
require.NoError(t, err)

infra, err := im.ProjectInfrastructure(context.Background(), prjConfig)
assert.NoError(t, err)

assert.NotNil(t, infra.cleanupDir, "should be a temp dir")

dir := infra.Options.Path
assert.FileExists(t, filepath.Join(dir, "main.bicep"))
assert.FileExists(t, filepath.Join(dir, "main.parameters.json"))
assert.FileExists(t, filepath.Join(dir, "resources.bicep"))

// Disable the alpha feature and check that the temp dir is not created
alpha.SetDefaultEnablement(string(alpha.Compose), false)

infra, err = im.ProjectInfrastructure(context.Background(), prjConfig)
assert.NoError(t, err)
assert.Empty(t, infra.Options.Path, "should be left empty")
}

func TestImportManager_SynthAllInfrastructure_FromResources(t *testing.T) {
alpha.SetDefaultEnablement(string(alpha.Compose), true)
t.Cleanup(func() { alpha.SetDefaultEnablement(string(alpha.Compose), false) })

im := &ImportManager{
dotNetImporter: &DotNetImporter{
alphaFeatureManager: alpha.NewFeaturesManagerWithConfig(config.NewEmptyConfig()),
},
}

prjConfig := &ProjectConfig{}
err := yaml.Unmarshal([]byte(prjWithResources), prjConfig)
require.NoError(t, err)

projectFs, err := im.SynthAllInfrastructure(context.Background(), prjConfig)
require.NoError(t, err)

files := []string{
"main.bicep",
"main.parameters.json",
"resources.bicep",
}
for _, f := range files {
_, err := projectFs.Open(filepath.Join(DefaultPath, f))
assert.NoError(t, err, "file %s should exist", f)
}

// Disable the alpha feature
alpha.SetDefaultEnablement(string(alpha.Compose), false)

_, err = im.SynthAllInfrastructure(context.Background(), prjConfig)
assert.Error(t, err)
}
Loading

0 comments on commit e2b12da

Please sign in to comment.