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 Java analyzer #4422

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
849de05
Add a new child action "orchestrate". And create a sample azure.yaml …
rujche Sep 6, 2024
acee60b
Get list of pom files.
rujche Sep 9, 2024
88d4d1b
add the code for azd java analyzer
saragluna Sep 10, 2024
6ba0816
Hook the java analyzer with the azd init.
saragluna Sep 11, 2024
6a4b664
Enhance
saragluna Sep 11, 2024
bafcbc4
Implement java_project_bicep_file_generator.go.
rujche Sep 11, 2024
b69a47e
Improve log: Add information about the file path.
rujche Sep 11, 2024
69bc59a
Enhance the java analyzer
saragluna Sep 13, 2024
8d67fc5
Add feature: Support add mysql when run "azd init".
rujche Sep 13, 2024
91900aa
Delete java_project_bicep_file_generator.go.
rujche Sep 13, 2024
2da438b
Add logic about accessing mysql in aca.
rujche Sep 14, 2024
f4e00ef
Merge pull request #1 from rujche/rujche/java-analyzer
saragluna Sep 14, 2024
2aa6cac
Fix typo by changing "DbMysql" to "DbMySql".
rujche Sep 14, 2024
cace7fa
Merge pull request #2 from rujche/rujche/java-analyzer
saragluna Sep 14, 2024
082b9b2
Use managed-identity instead of username and password. Now it has err…
rujche Sep 18, 2024
b0c39a6
Access MySql by managed identity instead of username&password.
rujche Sep 20, 2024
f90416f
Add Azure Deps in appdetect.go
saragluna Sep 23, 2024
84b7852
Customize the azd VS Code extension
saragluna Sep 23, 2024
6587f83
improve the java analyzer for event-driven
saragluna Sep 23, 2024
103a005
refactor the java analyzer
saragluna Sep 26, 2024
2e01347
Create service connector by bicep file.
rujche Sep 27, 2024
708681a
1. Remove duplicated 'azd extension add'.
rujche Sep 29, 2024
353a802
Update name of resources: linkerCreatorIdentity and appLinkToMySql
rujche Sep 29, 2024
6d853de
Merge remote-tracking branch 'saragluna/java-analyzer' into rujche/ja…
rujche Sep 29, 2024
85ec20b
Merge pull request #3 from rujche/rujche/java-analyzer
saragluna Sep 29, 2024
2856fb9
Merge remote-tracking branch 'saragluna/java-analyzer' into rujche/ja…
rujche Sep 29, 2024
b70878e
1. Add rule about postgresql in project_analyzer_java.go.
rujche Oct 7, 2024
437eeb6
Fix the error about CORS.
rujche Oct 7, 2024
3410c97
Merge pull request #4 from rujche/rujche/java-analyzer
saragluna Oct 8, 2024
f244293
Merge branch 'main' into saragluna/java-analyzer
rujche Oct 8, 2024
df2f5f2
add support for service bus
saragluna Oct 8, 2024
b6e6ecc
fix servicebus
saragluna Oct 8, 2024
073d857
Remove the logic of create tag after create service connector.
rujche Oct 8, 2024
ef73ce1
Merge remote-tracking branch 'saragluna/java-analyzer' into rujche/ja…
rujche Oct 8, 2024
4c84661
Merge pull request #5 from rujche/rujche/java-analyzer
rujche Oct 8, 2024
f1e2fc1
support both mi and connection string for service bus
saragluna Oct 9, 2024
23362f8
For PostgreSQL, support both password and passwordless.
rujche Oct 10, 2024
193f054
For MySQL, support both password and passwordless.
rujche Oct 11, 2024
48cbeb5
Remove logic of adding tag after creating service connector. Because …
rujche Oct 11, 2024
9f4fe27
Fix bug: create service connector only work for the first time run of…
rujche Oct 11, 2024
207ffa3
Merge pull request #6 from rujche/rujche/java-analyzer
rujche Oct 12, 2024
0594412
Add new feature: analyze project to add Mongo DB.
rujche Oct 12, 2024
c45382a
Delete unused content in main.bicept.
rujche Oct 12, 2024
dca325f
Merge pull request #7 from rujche/rujche/java-analyzer
rujche Oct 12, 2024
babf604
Fix bug: Get auth type should only be required for MySQL and PostgreSQL.
rujche Oct 14, 2024
12b169e
Merge pull request #8 from rujche/rujche/java-analyzer
rujche Oct 14, 2024
a2a3a73
Make sure app work well after deployed to ACA no matter what value "s…
rujche Oct 15, 2024
2f4d2e9
Merge pull request #9 from rujche/rujche/java-analyzer
rujche Oct 15, 2024
a83f7d7
Implement feature: detect port in Dockerfile.
rujche Oct 15, 2024
35f762c
Merge pull request #10 from rujche/rujche/java-analyzer
rujche Oct 15, 2024
de2e922
Implement feature: detect redis by analyzing pom file.
rujche Oct 15, 2024
8607043
Merge pull request #11 from rujche/rujche/java-analyzer
rujche Oct 15, 2024
a482496
Detect Mongo DB by dependency spring-boot-starter-data-mongodb-reacti…
rujche Oct 15, 2024
671902a
Merge pull request #12 from rujche/rujche/java-analyzer
rujche Oct 15, 2024
9c53e73
Support all kinds of properties file like application(-profile).yaml(…
rujche Oct 16, 2024
6e14a1b
Merge pull request #13 from rujche/rujche/java-analyzer
rujche Oct 16, 2024
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
96 changes: 96 additions & 0 deletions cli/azd/cmd/orchestrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"context"
"fmt"
"github.com/azure/azure-dev/cli/azd/cmd/actions"
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/spf13/cobra"
"os"
"path/filepath"
)

func newOrchestrateFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *orchestrateFlags {
flags := &orchestrateFlags{}
return flags
}

func newOrchestrateCmd() *cobra.Command {
return &cobra.Command{
Use: "orchestrate",
Short: "Orchestrate an existing application. (Beta)",
}
}

type orchestrateFlags struct {
global *internal.GlobalCommandOptions

Check failure on line 30 in cli/azd/cmd/orchestrate.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

field `global` is unused (unused)

Check failure on line 30 in cli/azd/cmd/orchestrate.go

View workflow job for this annotation

GitHub Actions / azd-lint (windows-latest)

field `global` is unused (unused)
}

type orchestrateAction struct {
}

func (action orchestrateAction) Run(ctx context.Context) (*actions.ActionResult, error) {
azureYamlFile, err := os.Create("azure.yaml")
if err != nil {
return nil, fmt.Errorf("creating azure.yaml: %w", err)
}
defer azureYamlFile.Close()

files, err := findPomFiles(".")
if err != nil {
fmt.Println("Error:", err)
return nil, fmt.Errorf("find pom files: %w", err)
}

for _, file := range files {
if _, err := azureYamlFile.WriteString(file + "\n"); err != nil {
return nil, fmt.Errorf("writing azure.yaml: %w", err)
}
}

if err := azureYamlFile.Sync(); err != nil {
return nil, fmt.Errorf("saving azure.yaml: %w", err)
}
return nil, nil
}

func newOrchestrateAction() actions.Action {
return &orchestrateAction{}
}

func getCmdOrchestrateHelpDescription(*cobra.Command) string {
return generateCmdHelpDescription("Orchestrate an existing application in your current directory.",
[]string{
formatHelpNote(
fmt.Sprintf("Running %s without flags specified will prompt "+
"you to orchestrate using your existing code.",
output.WithHighLightFormat("orchestrate"),
)),
})
}

func getCmdOrchestrateHelpFooter(*cobra.Command) string {
return generateCmdHelpSamplesBlock(map[string]string{
"Orchestrate a existing project.": fmt.Sprintf("%s",

Check failure on line 78 in cli/azd/cmd/orchestrate.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

S1025: the argument is already a string, there's no need to use fmt.Sprintf (gosimple)

Check failure on line 78 in cli/azd/cmd/orchestrate.go

View workflow job for this annotation

GitHub Actions / azd-lint (windows-latest)

S1025: the argument is already a string, there's no need to use fmt.Sprintf (gosimple)
output.WithHighLightFormat("azd orchestrate"),
),
})
}

func findPomFiles(root string) ([]string, error) {
var files []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && filepath.Base(path) == "pom.xml" {
files = append(files, path)
}
return nil
})
return files, err
}
13 changes: 13 additions & 0 deletions cli/azd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ func NewRootCmd(
ActionResolver: newLogoutAction,
})

root.Add("orchestrate", &actions.ActionDescriptorOptions{
Command: newOrchestrateCmd(),
FlagsResolver: newOrchestrateFlags,
ActionResolver: newOrchestrateAction,
HelpOptions: actions.ActionHelpOptions{
Description: getCmdOrchestrateHelpDescription,
Footer: getCmdOrchestrateHelpFooter,
},
GroupingOptions: actions.CommandGroupOptions{
RootLevelHelp: actions.CmdGroupConfig,
},
})

root.Add("init", &actions.ActionDescriptorOptions{
Command: newInitCmd(),
FlagsResolver: newInitFlags,
Expand Down
66 changes: 66 additions & 0 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"path/filepath"

"github.com/azure/azure-dev/cli/azd/internal/appdetect/javaanalyze"
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet"
"github.com/bmatcuk/doublestar/v4"
Expand Down Expand Up @@ -131,6 +132,20 @@ func (db DatabaseDep) Display() string {
return ""
}

//type AzureDep string

type AzureDep interface {
ResourceDisplay() string
}

type AzureDepServiceBus struct {
Queues []string
}

func (a AzureDepServiceBus) ResourceDisplay() string {
return "Azure Service Bus"
}

type Project struct {
// The language associated with the project.
Language Language
Expand All @@ -141,6 +156,9 @@ 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

// The path to the project directory.
Path string

Expand Down Expand Up @@ -243,6 +261,9 @@ func detectUnder(ctx context.Context, root string, config detectConfig) ([]Proje
return nil, fmt.Errorf("scanning directories: %w", err)
}

// call the java analyzer
projects = analyze(projects)

return projects, nil
}

Expand Down Expand Up @@ -306,3 +327,48 @@ func walkDirectories(path string, fn walkDirFunc) error {

return nil
}

func analyze(projects []Project) []Project {
result := []Project{}
for _, project := range projects {
if project.Language == Java {
fmt.Printf("Java project [%s] found", project.Path)
_javaProjects := javaanalyze.Analyze(project.Path)

if len(_javaProjects) == 1 {
enrichFromJavaProject(_javaProjects[0], &project)
result = append(result, project)
} else {
for _, _project := range _javaProjects {
copiedProject := project
enrichFromJavaProject(_project, &copiedProject)
result = append(result, copiedProject)
}
}
} else {
result = append(result, project)
}
}
return result
}

func enrichFromJavaProject(azureYaml javaanalyze.AzureYaml, project *Project) {
// if there is only one project, we can safely assume that it is the main project
for _, resource := range azureYaml.Resources {
if resource.GetType() == "Azure Storage" {
// project.DatabaseDeps = append(project.DatabaseDeps, Db)
} else if resource.GetType() == "MySQL" {
project.DatabaseDeps = append(project.DatabaseDeps, DbMySql)
} else if resource.GetType() == "PostgreSQL" {
project.DatabaseDeps = append(project.DatabaseDeps, DbPostgres)
} else if resource.GetType() == "SQL Server" {
project.DatabaseDeps = append(project.DatabaseDeps, DbSqlServer)
} else if resource.GetType() == "Redis" {
project.DatabaseDeps = append(project.DatabaseDeps, DbRedis)
} else if resource.GetType() == "Azure Service Bus" {
project.AzureDeps = append(project.AzureDeps, AzureDepServiceBus{
Queues: resource.(*javaanalyze.ServiceBusResource).Queues,
})
}
}
}
91 changes: 91 additions & 0 deletions cli/azd/internal/appdetect/javaanalyze/azure_yaml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package javaanalyze

type AzureYaml struct {
Service *Service `json:"service"`
Resources []IResource `json:"resources"`
ServiceBindings []ServiceBinding `json:"serviceBindings"`
}

type IResource interface {
GetName() string
GetType() string
GetBicepParameters() []BicepParameter
GetBicepProperties() []BicepProperty
}

type Resource struct {
Name string `json:"name"`
Type string `json:"type"`
BicepParameters []BicepParameter `json:"bicepParameters"`
BicepProperties []BicepProperty `json:"bicepProperties"`
}

func (r *Resource) GetName() string {
return r.Name
}

func (r *Resource) GetType() string {
return r.Type
}

func (r *Resource) GetBicepParameters() []BicepParameter {
return r.BicepParameters
}

func (r *Resource) GetBicepProperties() []BicepProperty {
return r.BicepProperties
}

type ServiceBusResource struct {
Resource
Queues []string `json:"queues"`
TopicAndSubscriptions []string `json:"topicAndSubscriptions"`
}

type BicepParameter struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
}

type BicepProperty struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
}

type ResourceType int32

const (
RESOURCE_TYPE_MYSQL ResourceType = 0
RESOURCE_TYPE_AZURE_STORAGE ResourceType = 1
)

// Service represents a specific service's configuration.
type Service struct {
Name string `json:"name"`
Path string `json:"path"`
ResourceURI string `json:"resourceUri"`
Description string `json:"description"`
Environment []Environment `json:"environment"`
}

type Environment struct {
Name string `json:"name"`
Value string `json:"value"`
}

type ServiceBinding struct {
Name string `json:"name"`
ResourceURI string `json:"resourceUri"`
AuthType AuthType `json:"authType"`
}

type AuthType int32

const (
// Authentication type not specified.
AuthType_SYSTEM_MANAGED_IDENTITY AuthType = 0
// Username and Password Authentication.
AuthType_USER_PASSWORD AuthType = 1
)
39 changes: 39 additions & 0 deletions cli/azd/internal/appdetect/javaanalyze/project_analyzer_java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package javaanalyze

import "os"

type javaProject struct {
springProject springProject
mavenProject mavenProject
}

func Analyze(path string) []AzureYaml {
var result []AzureYaml
rules := []rule{
&ruleService{},
Comment on lines +12 to +13
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can replace these rules with switch statements like Python, and I feel comfortable moving all the logic here into java.go.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure

&ruleMysql{},
&rulePostgresql{},
&ruleStorage{},
&ruleServiceBusScsb{},
}

entries, err := os.ReadDir(path)
if err == nil {
for _, entry := range entries {
if "pom.xml" == entry.Name() {
mavenProjects, _ := analyzeMavenProject(path)

for _, mavenProject := range mavenProjects {
javaProject := &javaProject{
mavenProject: mavenProject,
springProject: analyzeSpringProject(mavenProject.path),
}
azureYaml, _ := applyRules(javaProject, rules)
result = append(result, *azureYaml)
}
}
}
}

return result
}
Loading
Loading