Skip to content

Commit 7084741

Browse files
🔧 [fix] Fix Port Mapping Issue (#62)
* 🐛 Fix: Add port initializiation for Docker Compose This commit adds significant logging for debugging as well as port initialization if a specified port is not found in the existing config file. * 🚑️ Add port validation before manuscript deployment This commit validates and assigns ports before deploying manuscripts, ensuring that ports for graphQL , the database, and the manuscript itself are all properly assigned. * ♻️ [chore] Move Port Verification to Pkg This commit moves the port verification out of deploy manuscript and into the common_command.go file in the pkg package. The idea would be to gradually migrate common methods to a single source so that the script is easier to follow and debug. * 🐛 [fix] Fix overlapping ports upon manuscript init This commit modifies GetListeningPorts() in common_command.go so as to make it more robust. Instead of just looking at system ports. It evaluates docker container ports and system ports comprehensively to ensure that port conflicts don't occur. It also continues the migration of system functionality to the common_command.go inside pkg package for better encapsulation. * 🎨 DRY out createDockerComposeFile function Applied principle of D.R.Y. - don't repeat yourself - to init_manuscripts.go. Now, instead of reusing similar logic to assign ports, it uses the common pkg.InitialzePorts function on the manuscript to assign new ports. This is a significantly cleaner implementation as it means that we'll only need to change the logic in one location going forward. * 🎨 Clean up duplicate FindAvailablePort definition Due to previous commmits, the FindAvailablePort() is now defined in pkg/common_command.go and no longer needs to be put here in init_manuscript.go. It is now being removed for clarity.
1 parent 2e9542e commit 7084741

File tree

3 files changed

+133
-70
lines changed

3 files changed

+133
-70
lines changed

commands/deploy_manuscript.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,20 @@ func DeployManuscript(args []string) {
6464
}
6565
return nil
6666
}},
67-
{"Step 2: Checking manuscript is already deployed", func() error {
67+
{"Step 2: Verifying Port Initialization", func() error { return pkg.InitializePorts(&ms) }},
68+
{"Step 3: Checking manuscript is already deployed", func() error {
6869
err := CheckManuscriptExist(ms)
6970
if err != nil {
7071
return err
7172
}
7273
return nil
7374
}},
74-
{"Step 3: Create Directory", func() error { return createDirectory(manuscriptDir) }},
75-
{"Step 4: Create ManuscriptFile", func() error { return copyManuscriptFile(manuscriptDir, manuscriptPath) }},
76-
{"Step 5: Create DockerComposeFile", func() error { return createDockerComposeFile(manuscriptDir, &ms) }},
77-
{"Step 6: Check Docker Installed", func() error { return checkDockerInstalled() }},
78-
{"Step 7: Start Docker Containers", func() error { return startDockerContainers(manuscriptDir) }},
79-
{"Step 8: Check Container Status", func() error { return checkContainerStatus(&ms) }},
75+
{"Step 4: Create Directory", func() error { return createDirectory(manuscriptDir) }},
76+
{"Step 5: Create ManuscriptFile", func() error { return copyManuscriptFile(manuscriptDir, manuscriptPath) }},
77+
{"Step 6: Create DockerComposeFile", func() error { return createDockerComposeFile(manuscriptDir, &ms) }},
78+
{"Step 7: Check Docker Installed", func() error { return checkDockerInstalled() }},
79+
{"Step 8: Start Docker Containers", func() error { return startDockerContainers(manuscriptDir) }},
80+
{"Step 9: Check Container Status", func() error { return checkContainerStatus(&ms) }},
8081
}
8182

8283
for _, step := range steps {

commands/init_manuscript.go

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -147,49 +147,50 @@ func createManuscriptFile(dir string, ms pkg.Manuscript) error {
147147
}
148148

149149
func createDockerComposeFile(dir string, ms *pkg.Manuscript) error {
150+
fmt.Printf("Debug: Creating docker-compose file in directory: %s\n", dir)
150151
composeFilePath := filepath.Join(dir, "docker-compose.yml")
151152
dockComposeTemplate := static.DockerComposeTemplate
152153
switch ms.Sink {
153154
case defaultSink:
154155
m, err := pkg.LoadConfig(manuscriptConfig)
155156
if err != nil {
156-
return fmt.Errorf("failed to load manuscript config: %w", err)
157+
fmt.Printf("Debug: Creating new config as none exists or error loading: %v\n", err)
158+
m = &pkg.Config{
159+
BaseDir: getHomeDir(),
160+
Manuscripts: []pkg.Manuscript{},
161+
}
157162
}
158163

159164
ms.GraphQLImage = graphQLImage
160165
if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
161166
ms.GraphQLImage = graphQLARMImage
162167
}
163168

164-
var excludePorts []int
165-
for _, m := range m.Manuscripts {
166-
if m.Name == ms.Name {
167-
ms.Port = m.Port
168-
ms.DbPort = m.DbPort
169-
ms.DbUser = m.DbUser
170-
ms.DbPassword = m.DbPassword
171-
ms.GraphQLPort = m.GraphQLPort
172-
dockComposeTemplate = static.DockerComposeWithPostgresqlContent
173-
return createTemplateFile(composeFilePath, dockComposeTemplate, ms)
169+
// Check if manuscript already exists in config
170+
var existingMs *pkg.Manuscript
171+
for _, manuscript := range m.Manuscripts {
172+
if manuscript.Name == ms.Name {
173+
existingMs = &manuscript
174+
break
174175
}
175-
excludePorts = append(excludePorts, m.Port, m.GraphQLPort)
176-
}
177-
port, err := FindAvailablePort(8081, 8181, excludePorts)
178-
if err != nil {
179-
return fmt.Errorf("failed to find available port: %w", err)
180-
}
181-
ms.Port = port
182-
excludePorts = append(excludePorts, port)
183-
graphQLPort, err := FindAvailablePort(8081, 8181, excludePorts)
184-
if err != nil {
185-
return fmt.Errorf("failed to find available port: %w", err)
186176
}
187-
dbPort, err := FindAvailablePort(15432, 15532, excludePorts)
188-
if err != nil {
189-
return fmt.Errorf("failed to find available port: %w", err)
177+
178+
if existingMs != nil {
179+
// Use existing ports if manuscript is already configured
180+
ms.Port = existingMs.Port
181+
ms.DbPort = existingMs.DbPort
182+
ms.DbUser = existingMs.DbUser
183+
ms.DbPassword = existingMs.DbPassword
184+
ms.GraphQLPort = existingMs.GraphQLPort
185+
} else {
186+
// Initialize new ports using the common function
187+
if err := pkg.InitializePorts(ms); err != nil {
188+
return fmt.Errorf("failed to initialize ports: %w", err)
189+
}
190190
}
191-
ms.DbPort = dbPort
192-
ms.GraphQLPort = graphQLPort
191+
192+
fmt.Printf("Debug: Using ports - Flink: %d, GraphQL: %d, DB: %d\n",
193+
ms.Port, ms.GraphQLPort, ms.DbPort)
193194
dockComposeTemplate = static.DockerComposeWithPostgresqlContent
194195
default:
195196
}
@@ -295,29 +296,6 @@ func createTemplateFile(filePath, tmplContent string, data interface{}) error {
295296
return nil
296297
}
297298

298-
func FindAvailablePort(startPort, endPort int, exclude []int) (int, error) {
299-
listeningPorts, err := pkg.GetListeningPorts()
300-
if err != nil {
301-
return 0, err
302-
}
303-
304-
portMap := make(map[int]bool)
305-
for _, port := range listeningPorts {
306-
portMap[port] = true
307-
}
308-
for _, port := range exclude {
309-
portMap[port] = true
310-
}
311-
312-
for port := startPort; port <= endPort; port++ {
313-
if !portMap[port] {
314-
return port, nil
315-
}
316-
}
317-
318-
return 0, fmt.Errorf("no available ports in the range %d-%d", startPort, endPort)
319-
}
320-
321299
func promptInput(prompt, defaultVal string) string {
322300
fmt.Printf("\r\033[33m%s\u001B[0m", prompt)
323301
reader := bufio.NewReader(os.Stdin)

pkg/common_command.go

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,123 @@ package pkg
33
import (
44
"bufio"
55
"bytes"
6+
"fmt"
67
"os/exec"
78
"regexp"
89
"strconv"
910
)
1011

1112
func GetListeningPorts() ([]int, error) {
13+
ports := make(map[int]bool)
14+
15+
// Check system ports using lsof
1216
cmd := exec.Command("lsof", "-nP", "-iTCP", "-sTCP:LISTEN")
1317
var out bytes.Buffer
1418
cmd.Stdout = &out
1519

1620
if err := cmd.Run(); err != nil {
17-
return nil, err
21+
// Don't return error here, continue to check Docker ports
22+
fmt.Printf("Warning: Unable to check system ports: %v\n", err)
23+
} else {
24+
re := regexp.MustCompile(`:(\d+)\s+\(LISTEN\)`)
25+
scanner := bufio.NewScanner(&out)
26+
for scanner.Scan() {
27+
line := scanner.Text()
28+
matches := re.FindStringSubmatch(line)
29+
if len(matches) > 1 {
30+
port, err := strconv.Atoi(matches[1])
31+
if err != nil {
32+
continue
33+
}
34+
ports[port] = true
35+
}
36+
}
1837
}
1938

20-
re := regexp.MustCompile(`:(\d+)\s+\(LISTEN\)`)
39+
// Check Docker container ports
40+
dockerCmd := exec.Command("docker", "ps", "--format", "{{.Ports}}")
41+
var dockerOut bytes.Buffer
42+
dockerCmd.Stdout = &dockerOut
2143

22-
var ports []int
23-
scanner := bufio.NewScanner(&out)
44+
if err := dockerCmd.Run(); err != nil {
45+
return nil, fmt.Errorf("failed to check Docker ports: %w", err)
46+
}
47+
48+
// Parse Docker port mappings
49+
scanner := bufio.NewScanner(&dockerOut)
50+
portRegex := regexp.MustCompile(`0\.0\.0\.0:(\d+)`)
2451
for scanner.Scan() {
2552
line := scanner.Text()
26-
matches := re.FindStringSubmatch(line)
27-
if len(matches) > 1 {
28-
port, err := strconv.Atoi(matches[1])
29-
if err != nil {
30-
continue
53+
matches := portRegex.FindAllStringSubmatch(line, -1)
54+
for _, match := range matches {
55+
if len(match) > 1 {
56+
port, err := strconv.Atoi(match[1])
57+
if err != nil {
58+
continue
59+
}
60+
ports[port] = true
3161
}
32-
ports = append(ports, port)
3362
}
3463
}
3564

36-
if err := scanner.Err(); err != nil {
37-
return nil, err
65+
// Convert map to slice
66+
var result []int
67+
for port := range ports {
68+
result = append(result, port)
69+
}
70+
return result, nil
71+
}
72+
73+
func InitializePorts(ms *Manuscript) error {
74+
// Initialize Flink port if not set
75+
if ms.Port == 0 {
76+
port, err := FindAvailablePort(8081, 8181, nil)
77+
if err != nil {
78+
return fmt.Errorf("failed to find available port for Flink: %w", err)
79+
}
80+
ms.Port = port
81+
}
82+
83+
// Initialize GraphQL port if not set
84+
if ms.GraphQLPort == 0 {
85+
graphQLPort, err := FindAvailablePort(8082, 8182, []int{ms.Port})
86+
if err != nil {
87+
return fmt.Errorf("failed to find available port for GraphQL: %w", err)
88+
}
89+
ms.GraphQLPort = graphQLPort
90+
}
91+
92+
// Initialize DB port if not set
93+
if ms.DbPort == 0 {
94+
dbPort, err := FindAvailablePort(15432, 15532, []int{ms.Port, ms.GraphQLPort})
95+
if err != nil {
96+
return fmt.Errorf("failed to find available port for DB: %w", err)
97+
}
98+
ms.DbPort = dbPort
99+
}
100+
101+
return nil
102+
}
103+
104+
func FindAvailablePort(startPort, endPort int, exclude []int) (int, error) {
105+
listeningPorts, err := GetListeningPorts()
106+
if err != nil {
107+
return 0, err
108+
}
109+
110+
portMap := make(map[int]bool)
111+
for _, port := range listeningPorts {
112+
portMap[port] = true
113+
}
114+
for _, port := range exclude {
115+
portMap[port] = true
116+
}
117+
118+
for port := startPort; port <= endPort; port++ {
119+
if !portMap[port] {
120+
return port, nil
121+
}
38122
}
39123

40-
return ports, nil
124+
return 0, fmt.Errorf("no available ports in the range %d-%d", startPort, endPort)
41125
}

0 commit comments

Comments
 (0)