Skip to content

extension: support existing resource for ai build start #5193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 17 additions & 0 deletions cli/azd/.vscode/cspell-azd-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ appinsightsexporter
appinsightsstorage
appplatform
appservice
aresource
arget
armapimanagement
armappconfiguration
Expand All @@ -41,6 +42,7 @@ azdcontext
azddeploy
azdev
azdexec
azdextb
azdinternal
azdtempl
azdtempl
Expand Down Expand Up @@ -97,20 +99,34 @@ dockerproject
doublestar
dskip
eastus
edisplay
eignore
eloading
Comment on lines +102 to +104
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are this used?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to add all of these new non-words?

Copy link
Contributor Author

@hemarina hemarina May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

endregion
entra
entraid
envlist
envname
envsubst
eresource
errcheck
errorinfo
errorlint
eselect
eselected
esubscription
euser
eventhubs
eworkflow
executil
fcreating
fenable
flexconsumption
floading
frequired
Frontends
fsnotify
fsubscription
funcapp
functestapp
functionapp
Expand Down Expand Up @@ -179,6 +195,7 @@ pflag
pgadmin
posix
preinit
protogen
proxying
psanford
psycopg
Expand Down
31 changes: 18 additions & 13 deletions cli/azd/extensions/microsoft.azd.ai.builder/internal/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,9 @@ func (a *startAction) Run(ctx context.Context, args []string) error {
if a.scenarioData.DatabaseType != "" {
desiredName := strings.ReplaceAll(a.scenarioData.DatabaseType, "db.", "")
dbResource := &azdext.ComposedResource{
Name: a.generateResourceName(desiredName),
Type: a.scenarioData.DatabaseType,
Name: a.generateResourceName(desiredName),
Type: a.scenarioData.DatabaseType,
ResourceId: a.scenarioData.DatabaseId,
}
resourcesToAdd[dbResource.Name] = dbResource
}
Expand All @@ -214,17 +215,19 @@ func (a *startAction) Run(ctx context.Context, args []string) error {
if a.scenarioData.MessagingType != "" {
desiredName := strings.ReplaceAll(a.scenarioData.MessagingType, "messaging.", "")
messagingResource := &azdext.ComposedResource{
Name: a.generateResourceName(desiredName),
Type: a.scenarioData.MessagingType,
Name: a.generateResourceName(desiredName),
Type: a.scenarioData.MessagingType,
ResourceId: a.scenarioData.MessagingId,
}
resourcesToAdd[messagingResource.Name] = messagingResource
}

// Add vector store resources
if a.scenarioData.VectorStoreType != "" {
vectorStoreResource := &azdext.ComposedResource{
Name: a.generateResourceName("vector-store"),
Type: a.scenarioData.VectorStoreType,
Name: a.generateResourceName("vector-store"),
Type: a.scenarioData.VectorStoreType,
ResourceId: a.scenarioData.VectorStoreId,
}
resourcesToAdd[vectorStoreResource.Name] = vectorStoreResource
}
Expand All @@ -244,9 +247,10 @@ func (a *startAction) Run(ctx context.Context, args []string) error {
}

storageResource := &azdext.ComposedResource{
Name: a.generateResourceName("storage"),
Type: "storage",
Config: storageConfigJson,
Name: a.generateResourceName("storage"),
Type: "storage",
Config: storageConfigJson,
ResourceId: a.scenarioData.StorageAccountId,
}

resourcesToAdd[storageResource.Name] = storageResource
Expand Down Expand Up @@ -343,10 +347,11 @@ func (a *startAction) Run(ctx context.Context, args []string) error {
}

appResource := &azdext.ComposedResource{
Name: a.generateResourceName(appKey),
Type: appType,
Config: appConfigJson,
Uses: []string{},
Name: a.generateResourceName(appKey),
Type: appType,
Config: appConfigJson,
Uses: []string{},
ResourceId: a.scenarioData.AppResourceIds[i],
}

serviceName := a.generateServiceName(appKey)
Expand Down
1 change: 1 addition & 0 deletions cli/azd/grpc/proto/compose.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ message ComposedResource {
string type = 2;
bytes config = 3;
repeated string uses = 4;
string resource_id = 5;
}

// ComposedResourceType represents a type of composability resource.
Expand Down
51 changes: 42 additions & 9 deletions cli/azd/internal/grpcserver/compose_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"fmt"

"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/environment/azdcontext"
"github.com/azure/azure-dev/cli/azd/pkg/infra"
"github.com/azure/azure-dev/cli/azd/pkg/lazy"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"google.golang.org/grpc/codes"
Expand All @@ -19,15 +21,20 @@ import (
// composeService exposes features of the AZD composability model to the Extensions Framework layer.
type composeService struct {
azdext.UnimplementedComposeServiceServer

lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext]
lazyEnv *lazy.Lazy[*environment.Environment]
lazyEnvManger *lazy.Lazy[environment.Manager]
}

func NewComposeService(
lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext],
lazyEnv *lazy.Lazy[*environment.Environment],
lazyEnvManger *lazy.Lazy[environment.Manager],
) azdext.ComposeServiceServer {
return &composeService{
lazyAzdContext: lazyAzdContext,
lazyEnv: lazyEnv,
lazyEnvManger: lazyEnvManger,
}
}

Expand All @@ -41,6 +48,16 @@ func (c *composeService) AddResource(
return nil, err
}

env, err := c.lazyEnv.GetValue()
if err != nil {
return nil, err
}

envManager, err := c.lazyEnvManger.GetValue()
if err != nil {
return nil, err
}

projectConfig, err := project.Load(ctx, azdContext.ProjectPath())
if err != nil {
return nil, err
Expand All @@ -56,10 +73,25 @@ func (c *composeService) AddResource(
}

projectConfig.Resources[req.Resource.Name] = &project.ResourceConfig{
Name: req.Resource.Name,
Type: project.ResourceType(req.Resource.Type),
Props: resourceProps,
Uses: req.Resource.Uses,
Name: req.Resource.Name,
Type: project.ResourceType(req.Resource.Type),
Props: resourceProps,
Uses: req.Resource.Uses,
ResourceId: req.Resource.ResourceId,
}

if req.Resource.ResourceId != "" {
// add existing:true to azure.yaml
if resource, exists := projectConfig.Resources[req.Resource.Name]; exists {
resource.Existing = true
}
// save resource id to env
env.DotenvSet(infra.ResourceIdName(req.Resource.Name), req.Resource.ResourceId)

err = envManager.Save(ctx, env)
if err != nil {
return nil, fmt.Errorf("saving environment: %w", err)
}
}

if err := project.Save(ctx, projectConfig, azdContext.ProjectPath()); err != nil {
Expand Down Expand Up @@ -162,10 +194,11 @@ func (c *composeService) ListResources(
return nil, fmt.Errorf("marshaling resource config: %w", err)
}
composedResource := &azdext.ComposedResource{
Name: resource.Name,
Type: string(resource.Type),
Config: resourceConfigBytes,
Uses: resource.Uses,
Name: resource.Name,
Type: string(resource.Type),
Config: resourceConfigBytes,
Uses: resource.Uses,
ResourceId: resource.ResourceId,
}
composedResources = append(composedResources, composedResource)
}
Expand Down
52 changes: 47 additions & 5 deletions cli/azd/internal/grpcserver/compose_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
"testing"

"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/environment/azdcontext"
"github.com/azure/azure-dev/cli/azd/pkg/lazy"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/azure/azure-dev/cli/azd/test/mocks"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockenv"
"github.com/stretchr/testify/require"
)

Expand All @@ -28,7 +30,15 @@ func Test_ComposeService_AddResource(t *testing.T) {
err := project.Save(*mockContext.Context, &projectConfig, azdCtx.ProjectPath())
require.NoError(t, err)
lazyAzdContext := lazy.From(azdCtx)
composeService := NewComposeService(lazyAzdContext)
env := environment.New("test")
envManager := &mockenv.MockEnvManager{}
lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})
lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})
composeService := NewComposeService(lazyAzdContext, lazyEnv, lazyEnvManager)

t.Run("success", func(t *testing.T) {
addReq := &azdext.AddResourceRequest{
Expand Down Expand Up @@ -85,7 +95,15 @@ func Test_ComposeService_GetResource(t *testing.T) {
err := project.Save(*mockContext.Context, &projectConfig, azdCtx.ProjectPath())
require.NoError(t, err)
lazyAzdContext := lazy.From(azdCtx)
composeService := NewComposeService(lazyAzdContext)
env := environment.New("test")
envManager := &mockenv.MockEnvManager{}
lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})
lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})
composeService := NewComposeService(lazyAzdContext, lazyEnv, lazyEnvManager)

t.Run("success", func(t *testing.T) {
getReq := &azdext.GetResourceRequest{
Expand Down Expand Up @@ -137,7 +155,15 @@ func Test_ComposeService_ListResources(t *testing.T) {
err := project.Save(*mockContext.Context, &projectConfig, azdCtx.ProjectPath())
require.NoError(t, err)
lazyAzdContext := lazy.From(azdCtx)
composeService := NewComposeService(lazyAzdContext)
env := environment.New("test")
envManager := &mockenv.MockEnvManager{}
lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})
lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})
composeService := NewComposeService(lazyAzdContext, lazyEnv, lazyEnvManager)

listResp, err := composeService.ListResources(*mockContext.Context, &azdext.EmptyRequest{})
require.NoError(t, err)
Expand All @@ -156,7 +182,15 @@ func Test_ComposeService_ListResources(t *testing.T) {
lazyAzdContext := lazy.NewLazy(func() (*azdcontext.AzdContext, error) {
return nil, azdcontext.ErrNoProject
})
composeService := NewComposeService(lazyAzdContext)
env := environment.New("test")
envManager := &mockenv.MockEnvManager{}
lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})
lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})
composeService := NewComposeService(lazyAzdContext, lazyEnv, lazyEnvManager)
_, err := composeService.ListResources(*mockContext.Context, &azdext.EmptyRequest{})
require.Error(t, err)
})
Expand All @@ -170,7 +204,15 @@ func Test_Test_ComposeService_ListResourceTypes(t *testing.T) {
})

// Create the service and call ListResourceTypes
service := NewComposeService(lazyAzdContext)
env := environment.New("test")
envManager := &mockenv.MockEnvManager{}
lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})
lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})
service := NewComposeService(lazyAzdContext, lazyEnv, lazyEnvManager)
response, err := service.ListResourceTypes(*mockContext.Context, &azdext.EmptyRequest{})
require.NoError(t, err)
require.NotNil(t, response)
Expand Down
Loading