Skip to content

Commit

Permalink
Support detect Azure Event Hubs - 2 (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
rujche authored Oct 30, 2024
1 parent a8f91b1 commit d992c70
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 48 deletions.
7 changes: 7 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ overrides:
- cloudapp
- mediaservices
- msecnd
- filename: internal/tracing/fields/fields.go
words:
- azuredeps
- filename: internal/appdetect/java.go
words:
- springframework
- eventhubs
- filename: docs/docgen.go
words:
- alexwolf
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func NewRootCmd(
Command: logout,
ActionResolver: newLogoutAction,
})

root.Add("init", &actions.ActionDescriptorOptions{
Command: newInitCmd(),
FlagsResolver: newInitFlags,
Expand Down
8 changes: 8 additions & 0 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ func (a AzureDepEventHubs) ResourceDisplay() string {
return "Azure Event Hubs"
}

type AzureDepStorageAccount struct {
ContainerNames []string
}

func (a AzureDepStorageAccount) ResourceDisplay() string {
return "Azure Storage Account"
}

type Project struct {
// The language associated with the project.
Language Language
Expand Down
36 changes: 30 additions & 6 deletions cli/azd/internal/appdetect/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ func readMavenProject(filePath string) (*mavenProject, error) {
func detectDependencies(mavenProject *mavenProject, project *Project) (*Project, error) {
// how can we tell it's a Spring Boot project?
// 1. It has a parent with a groupId of org.springframework.boot and an artifactId of spring-boot-starter-parent
// 2. It has a dependency with a groupId of org.springframework.boot and an artifactId that starts with spring-boot-starter
// 2. It has a dependency with a groupId of org.springframework.boot and an artifactId that starts with
// spring-boot-starter
isSpringBoot := false
if mavenProject.Parent.GroupId == "org.springframework.boot" && mavenProject.Parent.ArtifactId == "spring-boot-starter-parent" {
if mavenProject.Parent.GroupId == "org.springframework.boot" &&
mavenProject.Parent.ArtifactId == "spring-boot-starter-parent" {
isSpringBoot = true
}
for _, dep := range mavenProject.Dependencies {
Expand Down Expand Up @@ -181,14 +183,26 @@ func detectDependencies(mavenProject *mavenProject, project *Project) (*Project,

if dep.GroupId == "com.azure.spring" && dep.ArtifactId == "spring-cloud-azure-stream-binder-eventhubs" {
bindingDestinations := findBindingDestinations(applicationProperties)
destinations := make([]string, 0, len(bindingDestinations))
var destinations []string
containsInBinding := false
for bindingName, destination := range bindingDestinations {
destinations = append(destinations, destination)
log.Printf("Event Hubs [%s] found for binding [%s]", destination, bindingName)
if strings.Contains(bindingName, "-in-") { // Example: consume-in-0
containsInBinding = true
}
if !contains(destinations, destination) {
destinations = append(destinations, destination)
log.Printf("Event Hubs [%s] found for binding [%s]", destination, bindingName)
}
}
project.AzureDeps = append(project.AzureDeps, AzureDepEventHubs{
Names: destinations,
})
if containsInBinding {
project.AzureDeps = append(project.AzureDeps, AzureDepStorageAccount{
ContainerNames: []string{
applicationProperties["spring.cloud.azure.eventhubs.processor.checkpoint-store.container-name"]},
})
}
}
}

Expand All @@ -210,7 +224,8 @@ func readProperties(projectPath string) map[string]string {
readPropertiesInYamlFile(filepath.Join(projectPath, "/src/main/resources/application.yaml"), result)
profile, profileSet := result["spring.profiles.active"]
if profileSet {
readPropertiesInPropertiesFile(filepath.Join(projectPath, "/src/main/resources/application-"+profile+".properties"), result)
readPropertiesInPropertiesFile(
filepath.Join(projectPath, "/src/main/resources/application-"+profile+".properties"), result)
readPropertiesInYamlFile(filepath.Join(projectPath, "/src/main/resources/application-"+profile+".yml"), result)
readPropertiesInYamlFile(filepath.Join(projectPath, "/src/main/resources/application-"+profile+".yaml"), result)
}
Expand Down Expand Up @@ -321,3 +336,12 @@ func findBindingDestinations(properties map[string]string) map[string]string {

return result
}

func contains(array []string, str string) bool {
for _, v := range array {
if v == str {
return true
}
}
return false
}
5 changes: 3 additions & 2 deletions cli/azd/internal/repository/app_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ var dbMap = map[appdetect.DatabaseDep]struct{}{
}

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

// InitFromApp initializes the infra directory and project file from the current existing app.
Expand Down
55 changes: 24 additions & 31 deletions cli/azd/internal/repository/infra_confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ func (i *Initializer) infraSpecFromDetect(
serviceSpec.AzureServiceBus = spec.AzureServiceBus
case appdetect.AzureDepEventHubs:
serviceSpec.AzureEventHubs = spec.AzureEventHubs
case appdetect.AzureDepStorageAccount:
serviceSpec.AzureStorageAccount = spec.AzureStorageAccount
}
}
spec.Services = append(spec.Services, serviceSpec)
Expand Down Expand Up @@ -346,26 +348,36 @@ azureDepPrompt:
}
}

authType := scaffold.AuthType(0)
switch azureDep.(type) {
case appdetect.AzureDepServiceBus:
authType, err := i.chooseAuthType(ctx, azureDepName)
_authType, err := i.console.Prompt(ctx, input.ConsoleOptions{
Message: fmt.Sprintf("Input the authentication type you want for (%s), "+
"1 for connection string, 2 for managed identity", azureDep.ResourceDisplay()),
Help: "Authentication type:\n\n" +
"Enter 1 if you want to use connection string to connect to the Service Bus.\n" +
"Enter 2 if you want to use user assigned managed identity to connect to the Service Bus.",
})
if err != nil {
return err
}
spec.AzureServiceBus = &scaffold.AzureDepServiceBus{
Name: azureDepName,
Queues: azureDep.(appdetect.AzureDepServiceBus).Queues,
AuthUsingConnectionString: authType == scaffold.AuthType_PASSWORD,
AuthUsingManagedIdentity: authType == scaffold.AuthType_TOKEN_CREDENTIAL,

if _authType != "1" && _authType != "2" {
i.console.Message(ctx, "Invalid authentication type. Please enter 0 or 1.")
continue azureDepPrompt
}
case appdetect.AzureDepEventHubs:
authType, err := i.chooseAuthType(ctx, azureDepName)
if err != nil {
return err
if _authType == "1" {
authType = scaffold.AuthType_PASSWORD
} else {
authType = scaffold.AuthType_TOKEN_CREDENTIAL
}
spec.AzureEventHubs = &scaffold.AzureDepEventHubs{
}

switch dependency := azureDep.(type) {
case appdetect.AzureDepServiceBus:
spec.AzureServiceBus = &scaffold.AzureDepServiceBus{
Name: azureDepName,
EventHubNames: azureDep.(appdetect.AzureDepEventHubs).Names,
Queues: dependency.Queues,
AuthUsingConnectionString: authType == scaffold.AuthType_PASSWORD,
AuthUsingManagedIdentity: authType == scaffold.AuthType_TOKEN_CREDENTIAL,
}
Expand All @@ -375,22 +387,3 @@ azureDepPrompt:
}
return nil
}

func (i *Initializer) chooseAuthType(ctx context.Context, serviceName string) (scaffold.AuthType, error) {
portOptions := []string{
"User assigned managed identity",
"Connection string",
}
selection, err := i.console.Select(ctx, input.ConsoleOptions{
Message: "Choose auth type for '" + serviceName + "'?",
Options: portOptions,
})
if err != nil {
return scaffold.AUTH_TYPE_UNSPECIFIED, err
}
if selection == 0 {
return scaffold.AuthType_TOKEN_CREDENTIAL, nil
} else {
return scaffold.AuthType_PASSWORD, nil
}
}
21 changes: 13 additions & 8 deletions cli/azd/internal/scaffold/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ type InfraSpec struct {
DbCosmosMongo *DatabaseCosmosMongo
DbRedis *DatabaseRedis

// Azure Service Bus
AzureServiceBus *AzureDepServiceBus
// Azure EventHubs
AzureEventHubs *AzureDepEventHubs
AzureServiceBus *AzureDepServiceBus
AzureEventHubs *AzureDepEventHubs
AzureStorageAccount *AzureDepStorageAccount
}

type Parameter struct {
Expand Down Expand Up @@ -64,6 +63,13 @@ type AzureDepEventHubs struct {
AuthUsingManagedIdentity bool
}

type AzureDepStorageAccount struct {
Name string
ContainerNames []string
AuthUsingConnectionString bool
AuthUsingManagedIdentity bool
}

// AuthType defines different authentication types.
type AuthType int32

Expand Down Expand Up @@ -91,10 +97,9 @@ type ServiceSpec struct {
DbCosmosMongo *DatabaseReference
DbRedis *DatabaseReference

// Azure Service Bus
AzureServiceBus *AzureDepServiceBus
// Azure Service Bus
AzureEventHubs *AzureDepEventHubs
AzureServiceBus *AzureDepServiceBus
AzureEventHubs *AzureDepEventHubs
AzureStorageAccount *AzureDepStorageAccount
}

type Frontend struct {
Expand Down
51 changes: 51 additions & 0 deletions cli/azd/resources/scaffold/templates/resources.bicept
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,41 @@ module eventHubsConnectionString './modules/set-event-hubs-namespace-connection-
}
{{end}}
{{end}}
{{- if .AzureStorageAccount }}
var storageAccountName = '${abbrs.storageStorageAccounts}${resourceToken}'
module storageAccount 'br/public:avm/res/storage/storage-account:0.14.3' = {
name: 'storageAccount'
params: {
name: storageAccountName
publicNetworkAccess: 'Enabled'
blobServices: {
containers: [
{{- range $index, $element := .AzureStorageAccount.ContainerNames}}
{
name: '{{ $element }}'
}
{{- end}}
]
}
location: location
roleAssignments: [
{{- if (and .AzureStorageAccount .AzureStorageAccount.AuthUsingManagedIdentity) }}
{{- range .Services}}
{
principalId: {{bicepName .Name}}Identity.outputs.principalId
principalType: 'ServicePrincipal'
roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
}
{{- end}}
{{- end}}
]
networkAcls: {
defaultAction: 'Allow'
}
tags: tags
}
}
{{end}}
{{- range .Services}}

module {{bicepName .Name}}Identity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = {
Expand Down Expand Up @@ -363,6 +398,22 @@ module {{bicepName .Name}} 'br/public:avm/res/app/container-app:0.8.0' = {
value: ''
}
{{- end}}
{{- if .AzureStorageAccount }}
{
name: 'SPRING_CLOUD_AZURE_EVENTHUBS_PROCESSOR_CHECKPOINTSTORE_ACCOUNTNAME'
value: storageAccountName
}
{{- end}}
{{- if (and .AzureStorageAccount .AzureStorageAccount.AuthUsingManagedIdentity) }}
{
name: 'SPRING_CLOUD_AZURE_STORAGE_CREDENTIAL_MANAGEDIDENTITYENABLED'
value: 'true'
}
{
name: 'SPRING_CLOUD_AZURE_STORAGE_CREDENTIAL_CLIENTID'
value: {{bicepName .Name}}Identity.outputs.clientId
}
{{- end}}
{{- if .Frontend}}
{{- range $i, $e := .Frontend.Backends}}
{
Expand Down

0 comments on commit d992c70

Please sign in to comment.