Skip to content

Commit 0b20dee

Browse files
JeffreyCACopilot
andcommitted
Address feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f51e56c commit 0b20dee

6 files changed

Lines changed: 48 additions & 39 deletions

File tree

cli/azd/extensions/azure.ai.agents/internal/cmd/init.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func runInitFromManifest(
117117
}
118118

119119
// Get or create environment
120-
env := getExistingEnvironment(ctx, flags, azdClient)
120+
env := getExistingEnvironment(ctx, flags.env, azdClient)
121121
if env == nil {
122122
fmt.Println("Lets create a new default azd environment for your project.")
123123
env, err = createNewEnvironment(ctx, azdClient, flags.env)
@@ -498,15 +498,15 @@ func ensureProject(ctx context.Context, flags *initFlags, azdClient *azdext.AzdC
498498
return projectResponse.Project, nil
499499
}
500500

501-
func getExistingEnvironment(ctx context.Context, flags *initFlags, azdClient *azdext.AzdClient) *azdext.Environment {
501+
func getExistingEnvironment(ctx context.Context, envName string, azdClient *azdext.AzdClient) *azdext.Environment {
502502
var env *azdext.Environment
503-
if flags.env == "" {
503+
if envName == "" {
504504
if envResponse, err := azdClient.Environment().GetCurrent(ctx, &azdext.EmptyRequest{}); err == nil {
505505
env = envResponse.Environment
506506
}
507507
} else {
508508
if envResponse, err := azdClient.Environment().Get(ctx, &azdext.GetEnvironmentRequest{
509-
Name: flags.env,
509+
Name: envName,
510510
}); err == nil {
511511
env = envResponse.Environment
512512
}

cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_resources_helpers.go

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ func createNewEnvironment(
551551
envName string,
552552
) (*azdext.Environment, error) {
553553
if envName != "" {
554-
if existingEnv := getExistingEnvironment(ctx, &initFlags{env: envName}, azdClient); existingEnv != nil {
554+
if existingEnv := getExistingEnvironment(ctx, envName, azdClient); existingEnv != nil {
555555
return existingEnv, nil
556556
}
557557
}
@@ -575,8 +575,11 @@ func createNewEnvironment(
575575
if exterrors.IsCancellation(err) {
576576
return nil, exterrors.Cancelled("environment creation was cancelled")
577577
}
578-
if envName != "" && isEnvironmentAlreadyExistsError(err) {
579-
if existingEnv := getExistingEnvironment(ctx, &initFlags{env: envName}, azdClient); existingEnv != nil {
578+
// The workflow may have created the environment on disk before returning
579+
// an "already exists" error (e.g. a concurrent process raced, or a previous
580+
// partial run left env files behind). Re-fetch so we can reuse it.
581+
if envName != "" && status.Code(err) == codes.AlreadyExists {
582+
if existingEnv := getExistingEnvironment(ctx, envName, azdClient); existingEnv != nil {
580583
return existingEnv, nil
581584
}
582585
}
@@ -588,7 +591,7 @@ func createNewEnvironment(
588591
}
589592

590593
// Re-fetch the environment after creation
591-
env := getExistingEnvironment(ctx, &initFlags{env: envName}, azdClient)
594+
env := getExistingEnvironment(ctx, envName, azdClient)
592595
if env == nil {
593596
return nil, exterrors.Dependency(
594597
exterrors.CodeEnvironmentNotFound,
@@ -600,24 +603,6 @@ func createNewEnvironment(
600603
return env, nil
601604
}
602605

603-
// isEnvironmentAlreadyExistsError detects the duplicate-environment failure returned from
604-
// `azd env new`. Today the workflow surfaces this as a gRPC Internal error whose message
605-
// embeds the underlying azd error text, so we fall back to checking the status message
606-
// after confirming the status code when no dedicated AlreadyExists status is available.
607-
func isEnvironmentAlreadyExistsError(err error) bool {
608-
if status.Code(err) == codes.AlreadyExists {
609-
return true
610-
}
611-
612-
if status.Code(err) != codes.Internal {
613-
return false
614-
}
615-
616-
errText := strings.ToLower(status.Convert(err).Message())
617-
return strings.Contains(errText, "creating new environment: environment") &&
618-
strings.Contains(errText, "already exists")
619-
}
620-
621606
// loadAzureContext reads the current Azure context values (tenant, subscription, location)
622607
// from the azd environment and returns a populated AzureContext. Missing values are left empty.
623608
func loadAzureContext(

cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_resources_helpers_test.go

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

66
import (
77
"context"
8-
"fmt"
98
"net"
109
"testing"
1110

@@ -16,15 +15,6 @@ import (
1615
"google.golang.org/grpc/status"
1716
)
1817

19-
func duplicateEnvironmentWorkflowError(envName string) string {
20-
return fmt.Sprintf(
21-
"failed to run workflow: error executing step command 'env new %s': "+
22-
"creating new environment: environment '%s' already exists",
23-
envName,
24-
envName,
25-
)
26-
}
27-
2818
func TestExtractProjectDetails(t *testing.T) {
2919
t.Parallel()
3020

@@ -150,8 +140,8 @@ func TestCreateNewEnvironment_ReusesExistingEnvironmentAfterAlreadyExistsError(t
150140
}
151141
workflowServer := &testWorkflowServiceServer{
152142
runErr: status.Error(
153-
codes.Internal,
154-
duplicateEnvironmentWorkflowError(envName),
143+
codes.AlreadyExists,
144+
"environment already exists",
155145
),
156146
runHook: func() {
157147
envServer.environments[envName] = &azdext.Environment{Name: envName}

cli/azd/internal/grpcserver/workflow_service.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ package grpcserver
55

66
import (
77
"context"
8+
"errors"
89

910
"github.com/azure/azure-dev/cli/azd/pkg/azdext"
11+
"github.com/azure/azure-dev/cli/azd/pkg/environment"
1012
"github.com/azure/azure-dev/cli/azd/pkg/workflow"
1113
"google.golang.org/grpc/codes"
1214
"google.golang.org/grpc/status"
@@ -39,6 +41,9 @@ func (s *workflowService) Run(ctx context.Context, request *azdext.RunWorkflowRe
3941
}
4042

4143
if err := s.runner.Run(ctx, azdWorkflow); err != nil {
44+
if errors.Is(err, environment.ErrExists) {
45+
return nil, status.Errorf(codes.AlreadyExists, "failed to run workflow: %v", err)
46+
}
4247
return nil, status.Errorf(codes.Internal, "failed to run workflow: %v", err)
4348
}
4449

cli/azd/internal/grpcserver/workflow_service_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ package grpcserver
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"testing"
1011

1112
"github.com/azure/azure-dev/cli/azd/pkg/azdext"
13+
"github.com/azure/azure-dev/cli/azd/pkg/environment"
1214
"github.com/azure/azure-dev/cli/azd/pkg/workflow"
1315
"github.com/azure/azure-dev/cli/azd/test/mocks"
1416
"github.com/stretchr/testify/mock"
1517
"github.com/stretchr/testify/require"
18+
"google.golang.org/grpc/codes"
19+
"google.golang.org/grpc/status"
1620
)
1721

1822
func Test_WorkflowService_Run_Success(t *testing.T) {
@@ -78,11 +82,36 @@ func Test_WorkflowService_Run_Success(t *testing.T) {
7882

7983
// Assert
8084
require.Error(t, err)
85+
require.Equal(t, codes.Internal, status.Code(err))
8186
require.Nil(t, resp)
8287

8388
// Verify that the runner's ExecuteContext was invoked with the correct args.
8489
testRunner.AssertCalled(t, "ExecuteContext", contextType, []string{"provision"})
8590
})
91+
92+
t.Run("EnvironmentAlreadyExists", func(t *testing.T) {
93+
envExistsErr := fmt.Errorf("creating environment 'myenv': %w", environment.ErrExists)
94+
testRunner := &TestWorkflowRunner{}
95+
runner := workflow.NewRunner(testRunner, mockContext.Console)
96+
testRunner.On("ExecuteContext", contextType, mock.Anything).Return(envExistsErr)
97+
98+
service := NewWorkflowService(runner)
99+
100+
req := &azdext.RunWorkflowRequest{
101+
Workflow: &azdext.Workflow{
102+
Name: "env new",
103+
Steps: []*azdext.WorkflowStep{
104+
{Command: &azdext.WorkflowCommand{Args: []string{"env", "new", "myenv"}}},
105+
},
106+
},
107+
}
108+
109+
resp, err := service.Run(*mockContext.Context, req)
110+
111+
require.Error(t, err)
112+
require.Equal(t, codes.AlreadyExists, status.Code(err))
113+
require.Nil(t, resp)
114+
})
86115
}
87116

88117
// Updated TestWorkflowRunner using testify/mock.

cli/azd/pkg/environment/manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func (m *manager) Create(ctx context.Context, spec Spec) (*Environment, error) {
170170
case err != nil:
171171
return nil, fmt.Errorf("checking for existing environment: %w", err)
172172
default:
173-
return nil, fmt.Errorf("environment '%s' already exists", spec.Name)
173+
return nil, fmt.Errorf("%w: '%s'", ErrExists, spec.Name)
174174
}
175175

176176
env := New(spec.Name)

0 commit comments

Comments
 (0)