Skip to content

Commit 78e4e1d

Browse files
committed
compose if infra exists
1 parent 8c8fc36 commit 78e4e1d

File tree

4 files changed

+147
-40
lines changed

4 files changed

+147
-40
lines changed

cli/azd/pkg/azapi/test/main.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
9+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
10+
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
11+
)
12+
13+
func run() error {
14+
cred, err := azidentity.NewDefaultAzureCredential(nil)
15+
if err != nil {
16+
return fmt.Errorf("creating Azure credential: %w", err)
17+
}
18+
client, err := armresources.NewResourceGroupsClient("faa080af-c1d8-40ad-9cce-e1a450ca5b57", cred, nil)
19+
if err != nil {
20+
return fmt.Errorf("creating ResourceGroup client: %w", err)
21+
}
22+
23+
ctx := context.Background()
24+
poller, err := client.BeginDelete(ctx, "doesnotexist", nil)
25+
var respErr *azcore.ResponseError
26+
if errors.As(err, &respErr) && respErr.StatusCode == 404 { // Resource group is already deleted
27+
return nil
28+
}
29+
30+
if err != nil {
31+
return fmt.Errorf("beginning resource group deletion: %w", err)
32+
}
33+
34+
_, err = poller.PollUntilDone(ctx, nil)
35+
if err != nil {
36+
return fmt.Errorf("deleting resource group: %w", err)
37+
}
38+
39+
return nil
40+
}
41+
42+
func main() {
43+
err := run()
44+
if err != nil {
45+
fmt.Println(err)
46+
}
47+
}

cli/azd/pkg/project/importer.go

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package project
55

66
import (
77
"context"
8+
"errors"
89
"fmt"
910
"io/fs"
1011
"log"
@@ -15,6 +16,7 @@ import (
1516

1617
"github.com/azure/azure-dev/cli/azd/pkg/alpha"
1718
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning"
19+
"github.com/otiai10/copy"
1820
)
1921

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

144+
moduleExists, moduleErr := pathHasModule(infraRoot, projectConfig.Infra.Module)
145+
146+
composeEnabled := im.dotNetImporter.alphaFeatureManager.IsEnabled(featureCompose)
147+
if composeEnabled && len(projectConfig.Resources) > 0 {
148+
if moduleErr == nil && moduleExists {
149+
azdModuleExists, err := os.Stat(filepath.Join(infraRoot, "azd", "main.bicep"))
150+
if err != nil && !errors.Is(err, os.ErrNotExist) {
151+
return nil, fmt.Errorf("checking if module exists: %w", err)
152+
}
153+
154+
if azdModuleExists != nil {
155+
log.Printf("using infrastructure from %s directory", infraRoot)
156+
return &Infra{
157+
Options: projectConfig.Infra,
158+
}, nil
159+
}
160+
}
161+
162+
tmpDir, err := os.MkdirTemp("", "azd-infra")
163+
if err != nil {
164+
return nil, fmt.Errorf("creating temporary directory: %w", err)
165+
}
166+
167+
azdInfraDir := tmpDir
168+
if moduleErr == nil && moduleExists {
169+
// Copy the base infra directory
170+
if err := copy.Copy(infraRoot, tmpDir); err != nil {
171+
return nil, fmt.Errorf("copying infra directory: %w", err)
172+
}
173+
174+
azdInfraDir = filepath.Join(tmpDir, "azd")
175+
}
176+
177+
err = infraFsToDir(ctx, projectConfig, azdInfraDir)
178+
if err != nil {
179+
return nil, err
180+
}
181+
182+
return &Infra{
183+
Options: provisioning.Options{
184+
Provider: provisioning.Bicep,
185+
Path: tmpDir,
186+
Module: DefaultModule,
187+
},
188+
cleanupDir: tmpDir,
189+
}, nil
190+
}
191+
192+
if !composeEnabled && len(projectConfig.Resources) > 0 {
193+
return nil, fmt.Errorf(
194+
"compose is currently under alpha support and must be explicitly enabled."+
195+
" Run `%s` to enable this feature", alpha.GetEnableCommand(featureCompose))
196+
}
197+
142198
// Allow overriding the infrastructure only when path and module exists.
143-
if moduleExists, err := pathHasModule(infraRoot, projectConfig.Infra.Module); err == nil && moduleExists {
199+
if moduleErr == nil && moduleExists {
144200
log.Printf("using infrastructure from %s directory", infraRoot)
145201
return &Infra{
146202
Options: projectConfig.Infra,
@@ -165,17 +221,6 @@ func (im *ImportManager) ProjectInfrastructure(ctx context.Context, projectConfi
165221
}
166222
}
167223

168-
composeEnabled := im.dotNetImporter.alphaFeatureManager.IsEnabled(featureCompose)
169-
if composeEnabled && len(projectConfig.Resources) > 0 {
170-
return tempInfra(ctx, projectConfig)
171-
}
172-
173-
if !composeEnabled && len(projectConfig.Resources) > 0 {
174-
return nil, fmt.Errorf(
175-
"compose is currently under alpha support and must be explicitly enabled."+
176-
" Run `%s` to enable this feature", alpha.GetEnableCommand(featureCompose))
177-
}
178-
179224
return &Infra{}, nil
180225
}
181226

cli/azd/pkg/project/importer_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ func TestImportManagerProjectInfrastructure(t *testing.T) {
233233
lazyEnvManager: lazy.NewLazy(func() (environment.Manager, error) {
234234
return mockEnv, nil
235235
}),
236-
hostCheck: make(map[string]hostCheckResult),
236+
hostCheck: make(map[string]hostCheckResult),
237+
alphaFeatureManager: mockContext.AlphaFeaturesManager,
237238
})
238239

239240
// Do not use defaults

cli/azd/pkg/project/scaffold_gen.go

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ package project
55

66
import (
77
"context"
8+
"errors"
89
"fmt"
910
"io/fs"
11+
"log"
1012
"os"
1113
"path/filepath"
1214
"slices"
1315
"strings"
1416

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

41-
// Returns the infrastructure configuration that points to a temporary, generated `infra` directory on the filesystem.
42-
func tempInfra(
43-
ctx context.Context,
44-
prjConfig *ProjectConfig) (*Infra, error) {
45-
tmpDir, err := os.MkdirTemp("", "azd-infra")
46-
if err != nil {
47-
return nil, fmt.Errorf("creating temporary directory: %w", err)
48-
}
49-
42+
func infraFsToDir(ctx context.Context, prjConfig *ProjectConfig, dir string) error {
5043
files, err := infraFs(ctx, prjConfig)
5144
if err != nil {
52-
return nil, err
45+
return err
5346
}
5447

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

64-
target := filepath.Join(tmpDir, path)
57+
target := filepath.Join(dir, path)
6558
if err := os.MkdirAll(filepath.Dir(target), osutil.PermissionDirectoryOwnerOnly); err != nil {
6659
return err
6760
}
@@ -74,17 +67,10 @@ func tempInfra(
7467
return os.WriteFile(target, contents, d.Type().Perm())
7568
})
7669
if err != nil {
77-
return nil, fmt.Errorf("writing infrastructure: %w", err)
70+
return fmt.Errorf("writing infrastructure: %w", err)
7871
}
7972

80-
return &Infra{
81-
Options: provisioning.Options{
82-
Provider: provisioning.Bicep,
83-
Path: tmpDir,
84-
Module: DefaultModule,
85-
},
86-
cleanupDir: tmpDir,
87-
}, nil
73+
return nil
8874
}
8975

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

98-
infraPathPrefix := DefaultPath
84+
infraPrefix := DefaultPath
9985
if prjConfig.Infra.Path != "" {
100-
infraPathPrefix = prjConfig.Infra.Path
86+
infraPrefix = prjConfig.Infra.Path
87+
}
88+
89+
infraRoot := infraPrefix
90+
if !filepath.IsAbs(infraPrefix) {
91+
infraRoot = filepath.Join(prjConfig.Path, infraPrefix)
92+
}
93+
94+
infraDir, err := os.Stat(infraRoot)
95+
if !errors.Is(err, os.ErrNotExist) && err != nil {
96+
return nil, fmt.Errorf("error reading infra directory: %w", err)
97+
}
98+
99+
fi, err := os.Stat(filepath.Join(infraRoot, ".azure"))
100+
if !errors.Is(err, os.ErrNotExist) && err != nil {
101+
return nil, fmt.Errorf("error reading .azure file in infra: %w", err)
102+
}
103+
104+
if infraDir != nil && fi == nil { // if the infra directory is not managed by azd, generate it to infra/azd
105+
infraPrefix = filepath.Join(infraPrefix, "azd")
106+
log.Println("infrastructure directory is not managed by azd, generating to", infraPrefix)
101107
}
102108

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

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

124-
return generatedFS.WriteFile(filepath.Join(infraPathPrefix, path), contents, d.Type().Perm())
130+
return generatedFS.WriteFile(filepath.Join(infraPrefix, path), contents, d.Type().Perm())
125131
})
126132
if err != nil {
127-
return nil, err
133+
return nil, fmt.Errorf("generating: %w", err)
134+
}
135+
136+
if fi == nil {
137+
// create a sentinel file to indicate that the infra directory is managed by azd
138+
err = generatedFS.WriteFile(filepath.Join(infraPrefix, ".azure"), []byte{}, osutil.PermissionFileOwnerOnly)
139+
if err != nil {
140+
return nil, fmt.Errorf("writing sentinel: %w", err)
141+
}
128142
}
129143

130144
return generatedFS, nil

0 commit comments

Comments
 (0)