-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
easy-init: auto detect port from Dockerfile (#4454)
Use the `EXPOSE` directives in a Dockerfile to default the port configuration for the service. - When multiple `EXPOSE` ports are present, we will prompt the user for port selection. - When no Dockerfile or `EXPOSE` is present, the user will be asked as usual to provide a port number. Completes #4443
- Loading branch information
Showing
5 changed files
with
185 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,71 @@ | ||
package appdetect | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io/fs" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
func detectDocker(path string, entries []fs.DirEntry) (*Docker, error) { | ||
for _, entry := range entries { | ||
if strings.ToLower(entry.Name()) == "dockerfile" { | ||
return &Docker{ | ||
Path: filepath.Join(path, entry.Name()), | ||
}, nil | ||
dockerFilePath := filepath.Join(path, entry.Name()) | ||
return detectDockerFromFile(dockerFilePath) | ||
} | ||
} | ||
|
||
return nil, nil | ||
} | ||
|
||
func detectDockerFromFile(dockerFilePath string) (*Docker, error) { | ||
file, err := os.Open(dockerFilePath) | ||
if err != nil { | ||
return nil, fmt.Errorf("reading Dockerfile at %s: %w", dockerFilePath, err) | ||
} | ||
defer file.Close() | ||
scanner := bufio.NewScanner(file) | ||
|
||
var ports []Port | ||
for scanner.Scan() { | ||
line := strings.TrimSpace(scanner.Text()) | ||
if strings.HasPrefix(line, "EXPOSE") { | ||
parsedPorts, err := parsePortsInLine(line[len("EXPOSE"):]) | ||
if err != nil { | ||
log.Printf("parsing Dockerfile at %s: %v", dockerFilePath, err) | ||
} | ||
ports = append(ports, parsedPorts...) | ||
} | ||
} | ||
return &Docker{ | ||
Path: dockerFilePath, | ||
Ports: ports, | ||
}, nil | ||
} | ||
|
||
func parsePortsInLine(s string) ([]Port, error) { | ||
var ports []Port | ||
portSpecs := strings.Fields(s) | ||
for _, portSpec := range portSpecs { | ||
var portString string | ||
var protocol string | ||
if strings.Contains(portSpec, "/") { | ||
parts := strings.Split(portSpec, "/") | ||
portString = parts[0] | ||
protocol = parts[1] | ||
} else { | ||
portString = portSpec | ||
protocol = "tcp" | ||
} | ||
portNumber, err := strconv.Atoi(portString) | ||
if err != nil { | ||
return nil, fmt.Errorf("parsing port number: %w", err) | ||
} | ||
ports = append(ports, Port{portNumber, protocol}) | ||
} | ||
return ports, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package appdetect | ||
|
||
import ( | ||
"github.com/azure/azure-dev/cli/azd/pkg/osutil" | ||
"github.com/stretchr/testify/assert" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestParsePortsInLine(t *testing.T) { | ||
tests := []struct { | ||
portString string | ||
expectedPorts []Port | ||
}{ | ||
{"", nil}, | ||
{"80", []Port{{80, "tcp"}}}, | ||
{"80 3100", []Port{{80, "tcp"}, {3100, "tcp"}}}, | ||
{"80 3100/udp", []Port{{80, "tcp"}, {3100, "udp"}}}, | ||
{" 80/tcp 3100/udp ", []Port{{80, "tcp"}, {3100, "udp"}}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.portString, func(t *testing.T) { | ||
actual, err := parsePortsInLine(tt.portString) | ||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedPorts, actual) | ||
}) | ||
} | ||
} | ||
|
||
func TestDetectDockerFromFile(t *testing.T) { | ||
tests := []struct { | ||
dockerFileContent string | ||
expectedPorts []Port | ||
}{ | ||
{"", nil}, | ||
{"# EXPOSE 80", nil}, | ||
{"EXPOSE 80", []Port{{80, "tcp"}}}, | ||
{"EXPOSE 80 3100", []Port{{80, "tcp"}, {3100, "tcp"}}}, | ||
{"EXPOSE 80\nEXPOSE 3100", []Port{{80, "tcp"}, {3100, "tcp"}}}, | ||
{"EXPOSE 80/tcp\nEXPOSE 3100/udp", []Port{{80, "tcp"}, {3100, "udp"}}}, | ||
{"\n EXPOSE 80/tcp\n EXPOSE 3100/udp", []Port{{80, "tcp"}, {3100, "udp"}}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.dockerFileContent, func(t *testing.T) { | ||
tempDir := t.TempDir() | ||
tempFile := filepath.Join(tempDir, "Dockerfile") | ||
file, err := os.Create(tempFile) | ||
assert.NoError(t, err) | ||
file.Close() | ||
|
||
err = os.WriteFile(tempFile, []byte(tt.dockerFileContent), osutil.PermissionFile) | ||
assert.NoError(t, err) | ||
|
||
docker, err := detectDockerFromFile(tempFile) | ||
assert.NoError(t, err) | ||
actual := docker.Ports | ||
assert.Equal(t, tt.expectedPorts, actual) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters