diff --git a/.github/workflows/go-test-for-sjad-branch.yml b/.github/workflows/go-test-for-sjad-branch.yml
index c44aefe5b36..31b7c897341 100644
--- a/.github/workflows/go-test-for-sjad-branch.yml
+++ b/.github/workflows/go-test-for-sjad-branch.yml
@@ -39,4 +39,4 @@ jobs:
- name: Run tests
run: |
cd ./cli/azd
- go test $(go list ./... | grep -v github.com/azure/azure-dev/cli/azd/test/functional) -cover -v
+ go test $(go list ./... | grep -v github.com/azure/azure-dev/cli/azd/test/functional | grep -v github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning/terraform) -cover -v
diff --git a/cli/azd/.vscode/cspell.yaml b/cli/azd/.vscode/cspell.yaml
index 3c9b1f42d26..889925332db 100644
--- a/cli/azd/.vscode/cspell.yaml
+++ b/cli/azd/.vscode/cspell.yaml
@@ -26,6 +26,7 @@ words:
- sjad
- configserver
- chardata
+ - webflux
languageSettings:
- languageId: go
ignoreRegExpList:
diff --git a/cli/azd/internal/appdetect/appdetect.go b/cli/azd/internal/appdetect/appdetect.go
index 0fca45ca557..38bbc546824 100644
--- a/cli/azd/internal/appdetect/appdetect.go
+++ b/cli/azd/internal/appdetect/appdetect.go
@@ -195,7 +195,7 @@ func (a AzureDepStorageAccount) ResourceDisplay() string {
type Metadata struct {
ApplicationName string
- ServerPort string
+ ServerPort int
DatabaseNameInPropertySpringDatasourceUrl map[DatabaseDep]string
ContainsDependencySpringCloudAzureStarter bool
ContainsDependencySpringCloudAzureStarterJdbcPostgresql bool
@@ -204,6 +204,7 @@ type Metadata struct {
ContainsDependencySpringCloudEurekaClient bool
ContainsDependencySpringCloudConfigServer bool
ContainsDependencySpringCloudConfigClient bool
+ ContainsDependencyAboutEmbeddedWebServer bool
}
type Project struct {
diff --git a/cli/azd/internal/appdetect/appdetect_test.go b/cli/azd/internal/appdetect/appdetect_test.go
index 5dbd4be2b0e..9a0d14cfa4c 100644
--- a/cli/azd/internal/appdetect/appdetect_test.go
+++ b/cli/azd/internal/appdetect/appdetect_test.go
@@ -75,6 +75,9 @@ func TestDetect(t *testing.T) {
Options: map[string]interface{}{
JavaProjectOptionParentPomDir: filepath.Join(dir, "java-multimodules"),
},
+ Metadata: Metadata{
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
},
{
Language: JavaScript,
@@ -180,6 +183,9 @@ func TestDetect(t *testing.T) {
Options: map[string]interface{}{
JavaProjectOptionParentPomDir: filepath.Join(dir, "java-multimodules"),
},
+ Metadata: Metadata{
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
},
},
},
@@ -234,6 +240,9 @@ func TestDetect(t *testing.T) {
Options: map[string]interface{}{
JavaProjectOptionParentPomDir: filepath.Join(dir, "java-multimodules"),
},
+ Metadata: Metadata{
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
},
},
},
@@ -291,6 +300,9 @@ func TestDetect(t *testing.T) {
Options: map[string]interface{}{
JavaProjectOptionParentPomDir: filepath.Join(dir, "java-multimodules"),
},
+ Metadata: Metadata{
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
},
{
Language: Python,
diff --git a/cli/azd/internal/appdetect/spring_boot.go b/cli/azd/internal/appdetect/spring_boot.go
index a3b5e7d5fad..58250650881 100644
--- a/cli/azd/internal/appdetect/spring_boot.go
+++ b/cli/azd/internal/appdetect/spring_boot.go
@@ -6,6 +6,7 @@ import (
"maps"
"regexp"
"slices"
+ "strconv"
"strings"
)
@@ -407,6 +408,7 @@ func detectMetadata(azdProject *Project, springBootProject *SpringBootProject) {
detectDependencySpringCloudAzureStarterJdbcPostgresql(azdProject, springBootProject)
detectDependencySpringCloudConfig(azdProject, springBootProject)
detectDependencySpringCloudEureka(azdProject, springBootProject)
+ detectDependencyAboutEmbeddedWebServer(azdProject, springBootProject)
}
func detectPropertySpringCloudAzureCosmosDatabase(azdProject *Project, springBootProject *SpringBootProject) {
@@ -553,7 +555,11 @@ func detectPropertySpringApplicationName(azdProject *Project, springBootProject
func detectPropertyServerPort(azdProject *Project, springBootProject *SpringBootProject) {
var targetPropertyName = "server.port"
if serverPort, ok := springBootProject.applicationProperties[targetPropertyName]; ok {
- azdProject.Metadata.ServerPort = serverPort
+ if port, err := strconv.Atoi(serverPort); err == nil {
+ azdProject.Metadata.ServerPort = port
+ } else {
+ log.Printf("Failed to convert the value of server.port to int. %v.", err)
+ }
}
}
@@ -589,6 +595,25 @@ func detectDependencySpringCloudConfig(azdProject *Project, springBootProject *S
}
}
+func detectDependencyAboutEmbeddedWebServer(azdProject *Project, springBootProject *SpringBootProject) {
+ var targetGroupId = "org.springframework.boot"
+ var targetArtifactIds = []string{
+ "spring-boot-starter-web",
+ "spring-boot-starter-webflux",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-jetty",
+ "spring-boot-starter-undertow",
+ "spring-boot-starter-reactor-netty",
+ }
+ for _, targetArtifactId := range targetArtifactIds {
+ if hasDependency(springBootProject, targetGroupId, targetArtifactId) {
+ azdProject.Metadata.ContainsDependencyAboutEmbeddedWebServer = true
+ logMetadataUpdated("ContainsDependencyAboutEmbeddedWebServer = true")
+ return
+ }
+ }
+}
+
func logServiceAddedAccordingToMavenDependency(resourceName, groupId string, artifactId string) {
logServiceAddedAccordingToMavenDependencyAndExtraCondition(resourceName, groupId, artifactId, "")
}
diff --git a/cli/azd/internal/appdetect/spring_boot_test.go b/cli/azd/internal/appdetect/spring_boot_test.go
index ab049ffbe9e..d3120daf12b 100644
--- a/cli/azd/internal/appdetect/spring_boot_test.go
+++ b/cli/azd/internal/appdetect/spring_boot_test.go
@@ -1,7 +1,12 @@
package appdetect
import (
+ "context"
+ "path/filepath"
"testing"
+
+ "github.com/azure/azure-dev/cli/azd/pkg/exec"
+ "github.com/azure/azure-dev/cli/azd/pkg/tools/maven"
)
func TestGetDatabaseName(t *testing.T) {
@@ -62,3 +67,104 @@ func TestIsValidDatabaseName(t *testing.T) {
})
}
}
+
+func TestDetectDependencyAboutEmbeddedWebServer(t *testing.T) {
+ tests := []struct {
+ name string
+ testPoms []testPom
+ expected bool
+ }{
+ {
+ name: "no web dependency",
+ testPoms: []testPom{
+ {
+ pomFilePath: "pom.xml",
+ pomContentString: `
+
+ 4.0.0
+ com.example
+ example-project
+ 1.0.0
+
+ `,
+ },
+ },
+ expected: false,
+ },
+ {
+ name: "has dependency: spring-boot-starter-web",
+ testPoms: []testPom{
+ {
+ pomFilePath: "pom.xml",
+ pomContentString: `
+
+ 4.0.0
+ com.example
+ example-project
+ 1.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.0.0
+
+
+
+ `,
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "has dependency: spring-boot-starter-webflux",
+ testPoms: []testPom{
+ {
+ pomFilePath: "pom.xml",
+ pomContentString: `
+
+ 4.0.0
+ com.example
+ example-project
+ 1.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ 3.0.0
+
+
+
+ `,
+ },
+ },
+ expected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ workingDir, err := prepareTestPomFiles(tt.testPoms)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ for _, testPom := range tt.testPoms {
+ pomFilePath := filepath.Join(workingDir, testPom.pomFilePath)
+ mavenProject, err := createMavenProject(context.TODO(), maven.NewCli(exec.NewCommandRunner(nil)),
+ pomFilePath)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ project := Project{
+ Language: Java,
+ Path: pomFilePath,
+ DetectionRule: "Inferred by presence of: pom.xml",
+ }
+ detectAzureDependenciesByAnalyzingSpringBootProject(mavenProject, &project)
+ if project.Metadata.ContainsDependencyAboutEmbeddedWebServer != tt.expected {
+ t.Errorf("\nExpected: %v\nActual: %v", tt.expected,
+ project.Metadata.ContainsDependencyAboutEmbeddedWebServer)
+ }
+ }
+ })
+ }
+}
diff --git a/cli/azd/internal/repository/app_init_test.go b/cli/azd/internal/repository/app_init_test.go
index 8669c6a09cd..b75795c3146 100644
--- a/cli/azd/internal/repository/app_init_test.go
+++ b/cli/azd/internal/repository/app_init_test.go
@@ -3,7 +3,6 @@ package repository
import (
"context"
"fmt"
- "github.com/azure/azure-dev/cli/azd/internal/scaffold"
"os"
"path/filepath"
"strings"
@@ -11,6 +10,7 @@ import (
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/internal/appdetect"
+ "github.com/azure/azure-dev/cli/azd/internal/scaffold"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/stretchr/testify/require"
@@ -98,13 +98,7 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) {
},
},
},
- interactions: []string{
- // prompt for port -- hit multiple validation cases
- "notAnInteger",
- "-2",
- "65536",
- "1234",
- },
+ interactions: []string{},
want: project.ProjectConfig{
Services: map[string]*project.ServiceConfig{
"dotnet": {
@@ -118,11 +112,9 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) {
},
Resources: map[string]*project.ResourceConfig{
"dotnet": {
- Type: project.ResourceTypeHostContainerApp,
- Name: "dotnet",
- Props: project.ContainerAppProps{
- Port: 1234,
- },
+ Type: project.ResourceTypeHostContainerApp,
+ Name: "dotnet",
+ Props: project.ContainerAppProps{},
},
},
},
diff --git a/cli/azd/internal/repository/infra_confirm.go b/cli/azd/internal/repository/infra_confirm.go
index e76244ec8f9..98b9ee580d2 100644
--- a/cli/azd/internal/repository/infra_confirm.go
+++ b/cli/azd/internal/repository/infra_confirm.go
@@ -275,17 +275,10 @@ func GetOrPromptPort(
ctx context.Context,
name string,
svc appdetect.Project) (int, error) {
- if svc.Metadata.ServerPort != "" {
- return strconv.Atoi(svc.Metadata.ServerPort)
- }
if svc.Docker == nil || svc.Docker.Path == "" { // using default builder from azd
- if svc.Language == appdetect.Java || svc.Language == appdetect.DotNet {
- if svc.Metadata.ContainsDependencySpringCloudEurekaServer {
- return 8761, nil
- }
- if svc.Metadata.ContainsDependencySpringCloudConfigServer {
- return 8888, nil
- }
+ if svc.Language == appdetect.Java {
+ return getJavaApplicationPort(svc), nil
+ } else if svc.Language == appdetect.DotNet {
return 8080, nil
}
return 80, nil
@@ -296,12 +289,8 @@ func GetOrPromptPort(
switch len(ports) {
case 1: // only one port was exposed, that's the one
return ports[0].Number, nil
- case 0: // no ports exposed, prompt for port
- port, err := promptPortNumber(console, ctx, "What port does '"+name+"' listen on?")
- if err != nil {
- return -1, err
- }
- return port, nil
+ case 0: // no ports exposed, not expose port
+ return 0, nil
}
// multiple ports exposed, prompt for selection
@@ -332,6 +321,23 @@ func GetOrPromptPort(
return port, nil
}
+func getJavaApplicationPort(svc appdetect.Project) int {
+ if !shouldExposePort(svc) {
+ return 0
+ }
+ if svc.Metadata.ServerPort != 0 {
+ return svc.Metadata.ServerPort
+ } else {
+ return 8080
+ }
+}
+
+func shouldExposePort(svc appdetect.Project) bool {
+ return svc.Metadata.ContainsDependencySpringCloudEurekaServer ||
+ svc.Metadata.ContainsDependencySpringCloudConfigServer ||
+ svc.Metadata.ContainsDependencyAboutEmbeddedWebServer
+}
+
func (i *Initializer) buildInfraSpecByAzureDep(
ctx context.Context,
azureDep appdetect.AzureDep,
diff --git a/cli/azd/internal/repository/infra_confirm_test.go b/cli/azd/internal/repository/infra_confirm_test.go
index aeb7f2d89a5..ed6432c44cb 100644
--- a/cli/azd/internal/repository/infra_confirm_test.go
+++ b/cli/azd/internal/repository/infra_confirm_test.go
@@ -85,19 +85,11 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
},
},
- interactions: []string{
- // prompt for port -- hit multiple validation cases
- "notAnInteger",
- "-2",
- "65536",
- "1234",
- },
+ interactions: []string{},
want: scaffold.InfraSpec{
Services: []scaffold.ServiceSpec{
{
- Name: "dotnet",
- Port: 1234,
- Backend: &scaffold.Backend{},
+ Name: "dotnet",
},
},
},
@@ -308,3 +300,83 @@ func TestDetectCosmosSqlDatabaseContainerInFile(t *testing.T) {
})
}
}
+
+func Test_getJavaApplicationPort(t *testing.T) {
+ tests := []struct {
+ name string
+ svc appdetect.Project
+ expected int
+ }{
+ {
+ name: "not configure anything",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{},
+ },
+ expected: 0,
+ },
+ {
+ name: "only configure ServerPort",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ServerPort: 8888,
+ },
+ },
+ expected: 0,
+ },
+ {
+ name: "only configure ContainsDependencySpringCloudEurekaServer",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ContainsDependencySpringCloudEurekaServer: true,
+ },
+ },
+ expected: 8080,
+ },
+ {
+ name: "only configure ContainsDependencySpringCloudConfigServer",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ContainsDependencySpringCloudConfigServer: true,
+ },
+ },
+ expected: 8080,
+ },
+ {
+ name: "only configure ContainsDependencyAboutEmbeddedWebServer",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
+ },
+ expected: 8080,
+ },
+ {
+ name: "configure multiple dependencies",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ContainsDependencySpringCloudEurekaServer: true,
+ ContainsDependencySpringCloudConfigServer: true,
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
+ },
+ expected: 8080,
+ },
+ {
+ name: "configure ServerPort and multiple dependencies",
+ svc: appdetect.Project{
+ Metadata: appdetect.Metadata{
+ ServerPort: 8888,
+ ContainsDependencySpringCloudEurekaServer: true,
+ ContainsDependencySpringCloudConfigServer: true,
+ ContainsDependencyAboutEmbeddedWebServer: true,
+ },
+ },
+ expected: 8888,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equal(t, tt.expected, getJavaApplicationPort(tt.svc))
+ })
+ }
+}
diff --git a/cli/azd/pkg/project/scaffold_gen.go b/cli/azd/pkg/project/scaffold_gen.go
index c6cda5c4b09..cc20456064d 100644
--- a/cli/azd/pkg/project/scaffold_gen.go
+++ b/cli/azd/pkg/project/scaffold_gen.go
@@ -397,8 +397,9 @@ func handleContainerAppProps(
}
port := props.Port
- if port < 1 || port > 65535 {
- return fmt.Errorf("port value %d for host %s must be between 1 and 65535", port, resourceConfig.Name)
+ if port < 0 || port > 65535 {
+ return fmt.Errorf("port value for '%s' must be between 0 and 65535 (port = 0 means ingress disabled), "+
+ "but it's %d ", resourceConfig.Name, port)
}
serviceSpec.Port = port
diff --git a/cli/azd/resources/scaffold/templates/resources.bicept b/cli/azd/resources/scaffold/templates/resources.bicept
index 8997db4c67e..5fd37dcd83e 100644
--- a/cli/azd/resources/scaffold/templates/resources.bicept
+++ b/cli/azd/resources/scaffold/templates/resources.bicept
@@ -550,7 +550,6 @@ module {{bicepName .Name}} 'br/public:avm/res/app/container-app:0.8.0' = {
name: '{{containerAppName .Name}}'
{{- if ne .Port 0}}
ingressTargetPort: {{.Port}}
- {{- end}}
{{- if (and .Backend .Backend.Frontends)}}
corsPolicy: {
allowedOrigins: [
@@ -563,6 +562,9 @@ module {{bicepName .Name}} 'br/public:avm/res/app/container-app:0.8.0' = {
]
}
{{- end}}
+ {{- else}}
+ disableIngress: true
+ {{- end}}
scaleMinReplicas: 1
scaleMaxReplicas: 10
secrets: {
diff --git a/schemas/alpha/azure.yaml.json b/schemas/alpha/azure.yaml.json
index 7611a0d23f7..02e9fa72880 100644
--- a/schemas/alpha/azure.yaml.json
+++ b/schemas/alpha/azure.yaml.json
@@ -1254,9 +1254,6 @@
"type": "object",
"description": "A Docker-based container app.",
"additionalProperties": false,
- "required": [
- "port"
- ],
"properties": {
"type": true,
"uses": true,