Skip to content
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

Add resource support for Azure Event Hubs #4646

Closed
Closed
Changes from 1 commit
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
Next Next commit
add azure event hubs
saragluna committed Dec 24, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 4d445ce2f184b961b361342bb8fa42f623c3f4aa
28 changes: 28 additions & 0 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
@@ -141,6 +141,12 @@ type Project struct {
// Experimental: Database dependencies inferred through heuristics while scanning dependencies in the project.
DatabaseDeps []DatabaseDep

// Experimental: Azure dependencies inferred through heuristics while scanning dependencies in the project.
AzureDeps []AzureDep

// Experimental: Metadata inferred through heuristics while scanning the project.
Metadata Metadata

// The path to the project directory.
Path string

@@ -151,6 +157,28 @@ type Project struct {
Docker *Docker
}

//type AzureDep string

type AzureDep interface {
ResourceDisplay() string
}

type AzureDepEventHubs struct {
EventHubsNamePropertyMap map[string]string
UseKafka bool
SpringBootVersion string
}

func (a AzureDepEventHubs) ResourceDisplay() string {
return "Azure Event Hubs"
}

type Metadata struct {
ContainsDependencySpringCloudAzureStarter bool
}

const UnknownSpringBootVersion string = "unknownSpringBootVersion"

func (p *Project) HasWebUIFramework() bool {
for _, f := range p.Dependencies {
if f.IsWebUIFramework() {
29 changes: 29 additions & 0 deletions cli/azd/internal/auth_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package internal

// AuthType defines different authentication types.
type AuthType string

const (
AuthTypeUnspecified AuthType = "unspecified"
// Username and password, or key based authentication
AuthTypePassword AuthType = "password"
// Connection string authentication
AuthTypeConnectionString AuthType = "connectionString"
// Microsoft EntraID token credential
AuthTypeUserAssignedManagedIdentity AuthType = "userAssignedManagedIdentity"
)

func GetAuthTypeDescription(authType AuthType) string {
switch authType {
case AuthTypeUnspecified:
return "Unspecified"
case AuthTypePassword:
return "Username and password"
case AuthTypeConnectionString:
return "Connection string"
case AuthTypeUserAssignedManagedIdentity:
return "User assigned managed identity"
default:
return "Unspecified"
}
}
180 changes: 179 additions & 1 deletion cli/azd/internal/repository/app_init.go
Original file line number Diff line number Diff line change
@@ -44,6 +44,10 @@

var featureCompose = alpha.MustFeatureKey("compose")

var azureDepMap = map[string]struct{}{
appdetect.AzureDepEventHubs{}.ResourceDisplay(): {},
}

// InitFromApp initializes the infra directory and project file from the current existing app.
func (i *Initializer) InitFromApp(
ctx context.Context,
@@ -120,10 +124,44 @@
i.console.StopSpinner(ctx, title, input.StepDone)

var prjAppHost []appdetect.Project
for _, prj := range projects {
for index, prj := range projects {
if prj.Language == appdetect.DotNetAppHost {
prjAppHost = append(prjAppHost, prj)
}

if prj.Language == appdetect.Java {
var hasKafkaDep bool
for depIndex, dep := range prj.AzureDeps {
if eventHubs, ok := dep.(appdetect.AzureDepEventHubs); ok {
// prompt spring boot version if not detected for kafka
if eventHubs.UseKafka {
hasKafkaDep = true
springBootVersion := eventHubs.SpringBootVersion
if springBootVersion == appdetect.UnknownSpringBootVersion {
springBootVersionInput, err := promptSpringBootVersion(i.console, ctx)
if err != nil {
return err
}
eventHubs.SpringBootVersion = springBootVersionInput
prj.AzureDeps[depIndex] = eventHubs
}
}
// prompt event hubs name if not detected
for property, eventHubsName := range eventHubs.EventHubsNamePropertyMap {
if eventHubsName == "" {
promptMissingPropertyAndExit(i.console, ctx, property)
}
}
}
}

if hasKafkaDep && !prj.Metadata.ContainsDependencySpringCloudAzureStarter {
err := processSpringCloudAzureDepByPrompt(i.console, ctx, &projects[index])
if err != nil {
return err
}
}
}
}

if len(prjAppHost) > 1 {
@@ -455,6 +493,39 @@
dbNames[database] = db.Name
}

for _, azureDepPair := range detect.AzureDeps {
azureDep := azureDepPair.first
authType, err := chooseAuthTypeByPrompt(
azureDep.ResourceDisplay(),
[]internal.AuthType{internal.AuthTypeUserAssignedManagedIdentity, internal.AuthTypeConnectionString},
ctx,
i.console)
if err != nil {
return config, err
}
switch azureDep := azureDep.(type) {
case appdetect.AzureDepEventHubs:
if azureDep.UseKafka {
config.Resources["kafka"] = &project.ResourceConfig{
Type: project.ResourceTypeMessagingKafka,
Props: project.KafkaProps{
Topics: distinctValues(azureDep.EventHubsNamePropertyMap),
AuthType: authType,
SpringBootVersion: azureDep.SpringBootVersion,
},
}
} else {
config.Resources["eventhubs"] = &project.ResourceConfig{
Type: project.ResourceTypeMessagingEventHubs,
Props: project.EventHubsProps{
EventHubNames: distinctValues(azureDep.EventHubsNamePropertyMap),
AuthType: authType,
},
}
}
}
}

backends := []*project.ResourceConfig{}
frontends := []*project.ResourceConfig{}

@@ -483,6 +554,17 @@
resSpec.Uses = append(resSpec.Uses, dbNames[db])
}

for _, azureDep := range svc.AzureDeps {
switch azureDep.(type) {

Check failure on line 558 in cli/azd/internal/repository/app_init.go

GitHub Actions / azd-lint (ubuntu-latest)

S1034: assigning the result of this type assertion to a variable (switch azureDep := azureDep.(type)) could eliminate type assertions in switch cases (gosimple)
case appdetect.AzureDepEventHubs:
if azureDep.(appdetect.AzureDepEventHubs).UseKafka {

Check failure on line 560 in cli/azd/internal/repository/app_init.go

GitHub Actions / azd-lint (ubuntu-latest)

S1034(related information): could eliminate this type assertion (gosimple)
resSpec.Uses = append(resSpec.Uses, "kafka")
} else {
resSpec.Uses = append(resSpec.Uses, "eventhubs")
}
}
}

resSpec.Name = name
resSpec.Props = props
config.Resources[name] = &resSpec
@@ -578,3 +660,99 @@

return svc, nil
}

func chooseAuthTypeByPrompt(
name string,
authOptions []internal.AuthType,
ctx context.Context,
console input.Console) (internal.AuthType, error) {
var options []string
for _, option := range authOptions {
options = append(options, internal.GetAuthTypeDescription(option))
}
selection, err := console.Select(ctx, input.ConsoleOptions{
Message: "Choose auth type for " + name + ":",
Options: options,
})
if err != nil {
return internal.AuthTypeUnspecified, err
}
return authOptions[selection], nil
}

func promptMissingPropertyAndExit(console input.Console, ctx context.Context, key string) {
console.Message(ctx, fmt.Sprintf("No value was provided for %s. Please update the configuration file "+
"(like application.properties or application.yaml) with a valid value.", key))
os.Exit(0)
}

func distinctValues(input map[string]string) []string {
valueSet := make(map[string]struct{})
for _, value := range input {
valueSet[value] = struct{}{}
}

var result []string
for value := range valueSet {
result = append(result, value)
}

return result
}

func processSpringCloudAzureDepByPrompt(console input.Console, ctx context.Context, project *appdetect.Project) error {
continueOption, err := console.Select(ctx, input.ConsoleOptions{
Message: "Detected Kafka dependency but no spring-cloud-azure-starter found. Select an option",
Options: []string{
"Exit then I will manually add this dependency",
"Continue without this dependency, and provision Azure Event Hubs for Kafka",
"Continue without this dependency, and not provision Azure Event Hubs for Kafka",
},
})
if err != nil {
return err
}

switch continueOption {
case 0:
console.Message(ctx, "you have to manually add dependency com.azure.spring:spring-cloud-azure-starter. "+
"And use right version according to this page: "+
"https://github.com/Azure/azure-sdk-for-java/wiki/Spring-Versions-Mapping")
os.Exit(0)
case 1:
return nil
case 2:
// remove Kafka Azure Dep
var result []appdetect.AzureDep
for _, dep := range project.AzureDeps {
if eventHubs, ok := dep.(appdetect.AzureDepEventHubs); !(ok && eventHubs.UseKafka) {
result = append(result, dep)
}
}
project.AzureDeps = result
return nil
}
return nil
}

func promptSpringBootVersion(console input.Console, ctx context.Context) (string, error) {
selection, err := console.Select(ctx, input.ConsoleOptions{
Message: "No spring boot version detected, what is your spring boot version?",
Options: []string{
"Spring Boot 2.x",
"Spring Boot 3.x",
},
})
if err != nil {
return "", err
}

switch selection {
case 0:
return "2.x", nil
case 1:
return "3.x", nil
default:
return appdetect.UnknownSpringBootVersion, nil
}
}
12 changes: 12 additions & 0 deletions cli/azd/internal/repository/detect_confirm.go
Original file line number Diff line number Diff line change
@@ -42,11 +42,17 @@ const (
EntryKindModified EntryKind = "modified"
)

type Pair struct {
first appdetect.AzureDep
second EntryKind
}

// detectConfirm handles prompting for confirming the detected services and databases
type detectConfirm struct {
// detected services and databases
Services []appdetect.Project
Databases map[appdetect.DatabaseDep]EntryKind
AzureDeps map[string]Pair

// the root directory of the project
root string
@@ -73,6 +79,12 @@ func (d *detectConfirm) Init(projects []appdetect.Project, root string) {
d.Databases[dbType] = EntryKindDetected
}
}

for _, azureDep := range project.AzureDeps {
if _, supported := azureDepMap[azureDep.ResourceDisplay()]; supported {
d.AzureDeps[azureDep.ResourceDisplay()] = Pair{azureDep, EntryKindDetected}
}
}
}

d.captureUsage(
23 changes: 14 additions & 9 deletions cli/azd/internal/repository/infra_confirm.go
Original file line number Diff line number Diff line change
@@ -85,19 +85,24 @@ func (i *Initializer) infraSpecFromDetect(

switch db {
case appdetect.DbMongo:
serviceSpec.DbCosmosMongo = &scaffold.DatabaseReference{
DatabaseName: spec.DbCosmosMongo.DatabaseName,
}
err = scaffold.BindToMongoDb(&serviceSpec, spec.DbCosmosMongo)
case appdetect.DbPostgres:
serviceSpec.DbPostgres = &scaffold.DatabaseReference{
DatabaseName: spec.DbPostgres.DatabaseName,
}
err = scaffold.BindToPostgres(&serviceSpec, spec.DbPostgres)
case appdetect.DbRedis:
serviceSpec.DbRedis = &scaffold.DatabaseReference{
DatabaseName: "redis",
}
err = scaffold.BindToRedis(&serviceSpec, spec.DbRedis)
}
}

for _, azureDep := range svc.AzureDeps {
switch azureDep.(type) {
case appdetect.AzureDepEventHubs:
err = scaffold.BindToEventHubs(&serviceSpec, spec.AzureEventHubs)
}
}

if err != nil {
return scaffold.InfraSpec{}, err
}
spec.Services = append(spec.Services, serviceSpec)
}

4 changes: 3 additions & 1 deletion cli/azd/internal/repository/infra_confirm_test.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import (
)

func TestInitializer_infraSpecFromDetect(t *testing.T) {
envs, _ := scaffold.GetServiceBindingEnvsForPostgres()
tests := []struct {
name string
detect detectConfirm
@@ -183,9 +184,10 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
},
},
DbPostgres: &scaffold.DatabaseReference{
DbPostgres: &scaffold.DatabasePostgres{
DatabaseName: "myappdb",
},
Envs: envs,
},
{
Name: "js",
Loading