Skip to content

Commit

Permalink
compose if infra exists
Browse files Browse the repository at this point in the history
  • Loading branch information
weikanglim committed Nov 18, 2024
1 parent 5f035a9 commit 8be2a88
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 40 deletions.
69 changes: 57 additions & 12 deletions cli/azd/pkg/project/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package project

import (
"context"
"errors"
"fmt"
"io/fs"
"log"
Expand All @@ -15,6 +16,7 @@ import (

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

type ImportManager struct {
Expand Down Expand Up @@ -139,8 +141,62 @@ func (im *ImportManager) ProjectInfrastructure(ctx context.Context, projectConfi
infraRoot = filepath.Join(projectConfig.Path, infraRoot)
}

moduleExists, moduleErr := pathHasModule(infraRoot, projectConfig.Infra.Module)

composeEnabled := im.dotNetImporter.alphaFeatureManager.IsEnabled(featureCompose)
if composeEnabled && len(projectConfig.Resources) > 0 {
if moduleErr == nil && moduleExists {
azdModuleExists, err := os.Stat(filepath.Join(infraRoot, "azd", "main.bicep"))
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("checking if module exists: %w", err)
}

if azdModuleExists != nil {
log.Printf("using infrastructure from %s directory", infraRoot)
return &Infra{
Options: projectConfig.Infra,
}, nil
}
}

tmpDir, err := os.MkdirTemp("", "azd-infra")
if err != nil {
return nil, fmt.Errorf("creating temporary directory: %w", err)
}

azdInfraDir := tmpDir
if moduleErr == nil && moduleExists {
// Copy the base infra directory
if err := copy.Copy(infraRoot, tmpDir); err != nil {
return nil, fmt.Errorf("copying infra directory: %w", err)
}

azdInfraDir = filepath.Join(tmpDir, "azd")
}

err = infraFsToDir(ctx, projectConfig, azdInfraDir)
if err != nil {
return nil, err
}

return &Infra{
Options: provisioning.Options{
Provider: provisioning.Bicep,
Path: tmpDir,
Module: DefaultModule,
},
cleanupDir: tmpDir,
}, nil
}

if !composeEnabled && len(projectConfig.Resources) > 0 {
return nil, fmt.Errorf(
"compose is currently under alpha support and must be explicitly enabled."+
" Run `%s` to enable this feature", alpha.GetEnableCommand(featureCompose))
}

// Allow overriding the infrastructure only when path and module exists.
if moduleExists, err := pathHasModule(infraRoot, projectConfig.Infra.Module); err == nil && moduleExists {
if moduleErr == nil && moduleExists {
log.Printf("using infrastructure from %s directory", infraRoot)
return &Infra{
Options: projectConfig.Infra,
Expand All @@ -165,17 +221,6 @@ func (im *ImportManager) ProjectInfrastructure(ctx context.Context, projectConfi
}
}

composeEnabled := im.dotNetImporter.alphaFeatureManager.IsEnabled(featureCompose)
if composeEnabled && len(projectConfig.Resources) > 0 {
return tempInfra(ctx, projectConfig)
}

if !composeEnabled && len(projectConfig.Resources) > 0 {
return nil, fmt.Errorf(
"compose is currently under alpha support and must be explicitly enabled."+
" Run `%s` to enable this feature", alpha.GetEnableCommand(featureCompose))
}

return &Infra{}, nil
}

Expand Down
3 changes: 2 additions & 1 deletion cli/azd/pkg/project/importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ func TestImportManagerProjectInfrastructure(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,
})

// Do not use defaults
Expand Down
68 changes: 41 additions & 27 deletions cli/azd/pkg/project/scaffold_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ package project

import (
"context"
"errors"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"slices"
"strings"

"github.com/azure/azure-dev/cli/azd/internal/scaffold"
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/psanford/memfs"
)
Expand All @@ -38,18 +39,10 @@ func infraFs(_ context.Context, prjConfig *ProjectConfig) (fs.FS, error) {
return files, nil
}

// Returns the infrastructure configuration that points to a temporary, generated `infra` directory on the filesystem.
func tempInfra(
ctx context.Context,
prjConfig *ProjectConfig) (*Infra, error) {
tmpDir, err := os.MkdirTemp("", "azd-infra")
if err != nil {
return nil, fmt.Errorf("creating temporary directory: %w", err)
}

func infraFsToDir(ctx context.Context, prjConfig *ProjectConfig, dir string) error {
files, err := infraFs(ctx, prjConfig)
if err != nil {
return nil, err
return err
}

err = fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error {
Expand All @@ -61,7 +54,7 @@ func tempInfra(
return nil
}

target := filepath.Join(tmpDir, path)
target := filepath.Join(dir, path)
if err := os.MkdirAll(filepath.Dir(target), osutil.PermissionDirectoryOwnerOnly); err != nil {
return err
}
Expand All @@ -74,17 +67,10 @@ func tempInfra(
return os.WriteFile(target, contents, d.Type().Perm())
})
if err != nil {
return nil, fmt.Errorf("writing infrastructure: %w", err)
return fmt.Errorf("writing infrastructure: %w", err)
}

return &Infra{
Options: provisioning.Options{
Provider: provisioning.Bicep,
Path: tmpDir,
Module: DefaultModule,
},
cleanupDir: tmpDir,
}, nil
return nil
}

// Generates the filesystem of all infrastructure files to be placed, rooted at the project directory.
Expand All @@ -95,13 +81,33 @@ func infraFsForProject(ctx context.Context, prjConfig *ProjectConfig) (fs.FS, er
return nil, err
}

infraPathPrefix := DefaultPath
infraPrefix := DefaultPath
if prjConfig.Infra.Path != "" {
infraPathPrefix = prjConfig.Infra.Path
infraPrefix = prjConfig.Infra.Path
}

infraRoot := infraPrefix
if !filepath.IsAbs(infraPrefix) {
infraRoot = filepath.Join(prjConfig.Path, infraPrefix)
}

infraDir, err := os.Stat(infraRoot)
if !errors.Is(err, os.ErrNotExist) && err != nil {
return nil, fmt.Errorf("error reading infra directory: %w", err)
}

fi, err := os.Stat(filepath.Join(infraRoot, ".azd"))
if !errors.Is(err, os.ErrNotExist) && err != nil {
return nil, fmt.Errorf("error reading .azd file in infra: %w", err)
}

if infraDir != nil && fi == nil { // if the infra directory is not managed by azd, generate it to infra/azd
infraPrefix = filepath.Join(infraPrefix, "azd")
log.Println("infrastructure directory is not managed by azd, generating to", infraPrefix)
}

// root the generated content at the project directory
generatedFS := memfs.New()
// root the generated content at the project directory
err = fs.WalkDir(infraFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
Expand All @@ -111,7 +117,7 @@ func infraFsForProject(ctx context.Context, prjConfig *ProjectConfig) (fs.FS, er
return nil
}

err = generatedFS.MkdirAll(filepath.Join(infraPathPrefix, filepath.Dir(path)), osutil.PermissionDirectoryOwnerOnly)
err = generatedFS.MkdirAll(filepath.Join(infraPrefix, filepath.Dir(path)), osutil.PermissionDirectoryOwnerOnly)
if err != nil {
return err
}
Expand All @@ -121,10 +127,18 @@ func infraFsForProject(ctx context.Context, prjConfig *ProjectConfig) (fs.FS, er
return err
}

return generatedFS.WriteFile(filepath.Join(infraPathPrefix, path), contents, d.Type().Perm())
return generatedFS.WriteFile(filepath.Join(infraPrefix, path), contents, d.Type().Perm())
})
if err != nil {
return nil, err
return nil, fmt.Errorf("generating: %w", err)
}

if fi == nil {
// create a sentinel file to indicate that the infra directory is managed by azd
err = generatedFS.WriteFile(filepath.Join(infraPrefix, ".azd"), []byte{}, osutil.PermissionFileOwnerOnly)
if err != nil {
return nil, fmt.Errorf("writing sentinel: %w", err)
}
}

return generatedFS, nil
Expand Down

0 comments on commit 8be2a88

Please sign in to comment.