Skip to content

Commit

Permalink
compose: service bus and event hubs (#4743)
Browse files Browse the repository at this point in the history
Add support for creating Service Bus and Event Hubs.

Addresses #4742
  • Loading branch information
weikanglim authored Feb 12, 2025
1 parent 91ff41d commit 908d888
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .vscode/cspell-schemas.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
eventhubs
postdeploy
postdown
postinfracreate
Expand All @@ -12,3 +13,4 @@ preinfradelete
preprovision
prerestore
preup
servicebus
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 @@ -104,6 +104,7 @@ envsubst
errcheck
errorinfo
errorlint
eventhubs
executil
flexconsumption
Frontends
Expand Down
4 changes: 4 additions & 0 deletions cli/azd/internal/cmd/add/add_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func Configure(
case project.ResourceTypeDbPostgres,
project.ResourceTypeDbMongo:
return fillDatabaseName(ctx, r, console, p)
case project.ResourceTypeMessagingEventHubs:
return fillEventHubs(ctx, r, console, p)
case project.ResourceTypeMessagingServiceBus:
return fillServiceBus(ctx, r, console, p)
case project.ResourceTypeDbRedis:
if _, exists := p.PrjConfig.Resources["redis"]; exists {
return nil, fmt.Errorf("only one Redis resource is allowed at this time")
Expand Down
84 changes: 84 additions & 0 deletions cli/azd/internal/cmd/add/add_configure_messaging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package add

import (
"context"
"fmt"

"github.com/azure/azure-dev/cli/azd/internal/names"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/project"
)

func fillEventHubs(
ctx context.Context,
r *project.ResourceConfig,
console input.Console,
p PromptOptions) (*project.ResourceConfig, error) {
r.Name = "event-hubs"

if _, exists := p.PrjConfig.Resources["event-hubs"]; exists {
return nil, fmt.Errorf("only one event hubs resource is allowed at this time")
}

for {
topicName, err := console.Prompt(ctx, input.ConsoleOptions{
Message: "Input the event hub name:",
Help: "Event hub name\n\n" +
"Name of the event hub that the app connects to. " +
"Also known as a Kafka topic.",
})
if err != nil {
return r, err
}

if err := names.ValidateLabelName(topicName); err != nil {
console.Message(ctx, err.Error())
continue
}

r.Props = project.EventHubsProps{
Hubs: []string{topicName},
}
break
}

return r, nil
}

func fillServiceBus(
ctx context.Context,
r *project.ResourceConfig,
console input.Console,
p PromptOptions) (*project.ResourceConfig, error) {
r.Name = "service-bus"

if _, exists := p.PrjConfig.Resources["service-bus"]; exists {
return nil, fmt.Errorf("only one service bus resource is allowed at this time")
}

for {
queueName, err := console.Prompt(ctx, input.ConsoleOptions{
Message: "Input the queue name:",
Help: "Service Bus queue name\n\n" +
"Name of the queue that the app connects to. ",
})
if err != nil {
return r, err
}

if err := names.ValidateLabelName(queueName); err != nil {
console.Message(ctx, err.Error())
continue
}

r.Props = project.ServiceBusProps{
Queues: []string{queueName},
}
break
}

return r, nil
}
6 changes: 2 additions & 4 deletions cli/azd/internal/cmd/add/add_configure_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ func fillStorageDetails(
r *project.ResourceConfig,
console input.Console,
p PromptOptions) (*project.ResourceConfig, error) {
r.Name = "storage"

if _, exists := p.PrjConfig.Resources["storage"]; exists {
return nil, fmt.Errorf("only one Storage resource is allowed at this time")
}

if r.Name == "" {
r.Name = "storage"
}

modelProps, ok := r.Props.(project.StorageProps)
if !ok {
return nil, fmt.Errorf("invalid resource properties")
Expand Down
12 changes: 12 additions & 0 deletions cli/azd/internal/cmd/add/add_preview.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ func Metadata(r *project.ResourceConfig) resourceMeta {
res.UseEnvVars = []string{
"AZURE_OPENAI_ENDPOINT",
}
case project.ResourceTypeMessagingEventHubs:
res.AzureResourceType = "Microsoft.EventHub/namespaces"
res.UseEnvVars = []string{
"AZURE_EVENT_HUBS_HOST",
"AZURE_EVENT_HUBS_NAME",
}
case project.ResourceTypeMessagingServiceBus:
res.AzureResourceType = "Microsoft.ServiceBus/namespaces"
res.UseEnvVars = []string{
"AZURE_SERVICE_BUS_HOST",
"AZURE_SERVICE_BUS_NAME",
}
case project.ResourceTypeStorage:
res.AzureResourceType = "Microsoft.Storage/storageAccounts"
res.UseEnvVars = []string{
Expand Down
26 changes: 26 additions & 0 deletions cli/azd/internal/cmd/add/add_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func (a *AddAction) selectMenu() []Menu {
{Namespace: "db", Label: "Database", SelectResource: selectDatabase},
{Namespace: "host", Label: "Host service"},
{Namespace: "ai.openai", Label: "Azure OpenAI", SelectResource: a.selectOpenAi},
{Namespace: "messaging", Label: "Messaging", SelectResource: selectMessaging},
{Namespace: "storage", Label: "Storage account", SelectResource: selectStorage},
}
}
Expand Down Expand Up @@ -61,6 +62,31 @@ func selectDatabase(
return r, nil
}

func selectMessaging(
console input.Console,
ctx context.Context,
p PromptOptions) (*project.ResourceConfig, error) {
resourceTypesDisplayMap := make(map[string]project.ResourceType)
for _, resourceType := range project.AllResourceTypes() {
if strings.HasPrefix(string(resourceType), "messaging.") {
resourceTypesDisplayMap[resourceType.String()] = resourceType
}
}

r := &project.ResourceConfig{}
resourceTypesDisplay := slices.Sorted(maps.Keys(resourceTypesDisplayMap))
dbOption, err := console.Select(ctx, input.ConsoleOptions{
Message: "Which type of messaging service?",
Options: resourceTypesDisplay,
})
if err != nil {
return nil, err
}

r.Type = resourceTypesDisplayMap[resourceTypesDisplay[dbOption]]
return r, nil
}

func selectStorage(
console input.Console,
ctx context.Context,
Expand Down
4 changes: 4 additions & 0 deletions cli/azd/internal/scaffold/scaffold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ func TestExecInfra(t *testing.T) {
DatabaseName: "appdb",
},
DbRedis: &DatabaseRedis{},
ServiceBus: &ServiceBus{},
EventHubs: &EventHubs{},
StorageAccount: &StorageAccount{},
Services: []ServiceSpec{
{
Expand All @@ -111,6 +113,8 @@ func TestExecInfra(t *testing.T) {
DbPostgres: &DatabaseReference{
DatabaseName: "appdb",
},
ServiceBus: &ServiceBus{},
EventHubs: &EventHubs{},
StorageAccount: &StorageReference{},
},
{
Expand Down
18 changes: 18 additions & 0 deletions cli/azd/internal/scaffold/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ type InfraSpec struct {
DbCosmosMongo *DatabaseCosmosMongo
DbRedis *DatabaseRedis

// Messaging services
ServiceBus *ServiceBus
EventHubs *EventHubs

// Storage account
StorageAccount *StorageAccount

// ai models
Expand Down Expand Up @@ -56,6 +61,15 @@ type AIModelModel struct {
Version string
}

type ServiceBus struct {
Queues []string
Topics []string
}

type EventHubs struct {
Hubs []string
}

type StorageAccount struct {
Containers []string
}
Expand All @@ -81,6 +95,10 @@ type ServiceSpec struct {

// AI model connections
AIModels []AIModelReference

// Messaging services
ServiceBus *ServiceBus
EventHubs *EventHubs
}

type Frontend struct {
Expand Down
3 changes: 3 additions & 0 deletions cli/azd/pkg/azapi/azure_resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
AzureResourceTypeCacheForRedis AzureResourceType = "Microsoft.Cache/redis"
AzureResourceTypeCDNProfile AzureResourceType = "Microsoft.Cdn/profiles"
AzureResourceTypeCosmosDb AzureResourceType = "Microsoft.DocumentDB/databaseAccounts"
AzureResourceTypeEventHubsNamespace AzureResourceType = "Microsoft.EventHub/namespaces"
AzureResourceTypeContainerApp AzureResourceType = "Microsoft.App/containerApps"
AzureResourceTypeSpringApp AzureResourceType = "Microsoft.AppPlatform/Spring"
AzureResourceTypeContainerAppEnvironment AzureResourceType = "Microsoft.App/managedEnvironments"
Expand Down Expand Up @@ -79,6 +80,8 @@ func GetResourceTypeDisplayName(resourceType AzureResourceType) string {
return "Container Apps Environment"
case AzureResourceTypeServiceBusNamespace:
return "Service Bus Namespace"
case AzureResourceTypeEventHubsNamespace:
return "Event Hubs Namespace"
case AzureResourceTypeServicePlan:
return "App Service plan"
case AzureResourceTypeCosmosDb:
Expand Down
65 changes: 47 additions & 18 deletions cli/azd/pkg/project/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ func AllResourceTypes() []ResourceType {
ResourceTypeDbMongo,
ResourceTypeHostContainerApp,
ResourceTypeOpenAiModel,
ResourceTypeMessagingEventHubs,
ResourceTypeMessagingServiceBus,
ResourceTypeStorage,
}
}

const (
ResourceTypeDbRedis ResourceType = "db.redis"
ResourceTypeDbPostgres ResourceType = "db.postgres"
ResourceTypeDbMongo ResourceType = "db.mongo"
ResourceTypeHostContainerApp ResourceType = "host.containerapp"
ResourceTypeOpenAiModel ResourceType = "ai.openai.model"
ResourceTypeStorage ResourceType = "storage"
ResourceTypeDbRedis ResourceType = "db.redis"
ResourceTypeDbPostgres ResourceType = "db.postgres"
ResourceTypeDbMongo ResourceType = "db.mongo"
ResourceTypeHostContainerApp ResourceType = "host.containerapp"
ResourceTypeOpenAiModel ResourceType = "ai.openai.model"
ResourceTypeMessagingEventHubs ResourceType = "messaging.eventhubs"
ResourceTypeMessagingServiceBus ResourceType = "messaging.servicebus"
ResourceTypeStorage ResourceType = "storage"
)

func (r ResourceType) String() string {
Expand All @@ -43,6 +47,10 @@ func (r ResourceType) String() string {
return "Container App"
case ResourceTypeOpenAiModel:
return "Open AI Model"
case ResourceTypeMessagingEventHubs:
return "Event Hubs"
case ResourceTypeMessagingServiceBus:
return "Service Bus"
case ResourceTypeStorage:
return "Storage Account"
}
Expand Down Expand Up @@ -82,22 +90,22 @@ func (r *ResourceConfig) MarshalYAML() (interface{}, error) {
return nil
}

var errMarshal error
switch raw.Type {
case ResourceTypeOpenAiModel:
err := marshalRawProps(raw.Props.(AIModelProps))
if err != nil {
return nil, err
}
errMarshal = marshalRawProps(raw.Props.(AIModelProps))
case ResourceTypeHostContainerApp:
err := marshalRawProps(raw.Props.(ContainerAppProps))
if err != nil {
return nil, err
}
errMarshal = marshalRawProps(raw.Props.(ContainerAppProps))
case ResourceTypeMessagingEventHubs:
errMarshal = marshalRawProps(raw.Props.(EventHubsProps))
case ResourceTypeMessagingServiceBus:
errMarshal = marshalRawProps(raw.Props.(ServiceBusProps))
case ResourceTypeStorage:
err := marshalRawProps(raw.Props.(StorageProps))
if err != nil {
return nil, err
}
errMarshal = marshalRawProps(raw.Props.(StorageProps))
}

if errMarshal != nil {
return nil, errMarshal
}

return raw, nil
Expand Down Expand Up @@ -137,6 +145,18 @@ func (r *ResourceConfig) UnmarshalYAML(value *yaml.Node) error {
return err
}
raw.Props = cap
case ResourceTypeMessagingEventHubs:
ehp := EventHubsProps{}
if err := unmarshalProps(&ehp); err != nil {
return err
}
raw.Props = ehp
case ResourceTypeMessagingServiceBus:
sbp := ServiceBusProps{}
if err := unmarshalProps(&sbp); err != nil {
return err
}
raw.Props = sbp
case ResourceTypeStorage:
sp := StorageProps{}
if err := unmarshalProps(&sp); err != nil {
Expand Down Expand Up @@ -171,6 +191,15 @@ type AIModelPropsModel struct {
Version string `yaml:"version,omitempty"`
}

type ServiceBusProps struct {
Queues []string `yaml:"queues,omitempty"`
Topics []string `yaml:"topics,omitempty"`
}

type EventHubsProps struct {
Hubs []string `yaml:"hubs,omitempty"`
}

type StorageProps struct {
Containers []string `yaml:"containers,omitempty"`
}
Loading

0 comments on commit 908d888

Please sign in to comment.