• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lntest/port/port.go
1
package port
2

3
import (
4
        "fmt"
5
        "net"
6
        "os"
7
        "path/filepath"
8
        "strconv"
9
        "sync"
10
        "time"
11

12
        "github.com/lightningnetwork/lnd/lntest/wait"
13
)
14

15
const (
16
        // ListenerFormat is the format string that is used to generate local
17
        // listener addresses.
18
        ListenerFormat = "127.0.0.1:%d"
19

20
        // defaultNodePort is the start of the range for listening ports of
21
        // harness nodes. Ports are monotonically increasing starting from this
22
        // number and are determined by the results of NextAvailablePort().
23
        defaultNodePort int = 10000
24

25
        // uniquePortFile is the name of the file that is used to store the
26
        // last port that was used by a node. This is used to make sure that
27
        // the same port is not used by multiple nodes at the same time. The
28
        // file is located in the temp directory of a system.
29
        uniquePortFile = "rpctest-port"
30
)
31

32
var (
33
        // portFileMutex is a mutex that is used to make sure that the port file
34
        // is not accessed by multiple goroutines of the same process at the
35
        // same time. This is used in conjunction with the lock file to make
36
        // sure that the port file is not accessed by multiple processes at the
37
        // same time either. So the lock file is to guard between processes and
38
        // the mutex is to guard between goroutines of the same process.
39
        portFileMutex sync.Mutex
40
)
41

42
// NextAvailablePort returns the first port that is available for listening by a
43
// new node, using a lock file to make sure concurrent access for parallel tasks
44
// on the same system don't re-use the same port.
UNCOV
45
func NextAvailablePort() int {
×
UNCOV
46
        portFileMutex.Lock()
×
UNCOV
47
        defer portFileMutex.Unlock()
×
UNCOV
48

×
UNCOV
49
        lockFile := filepath.Join(os.TempDir(), uniquePortFile+".lock")
×
UNCOV
50
        timeout := time.After(wait.DefaultTimeout)
×
UNCOV
51

×
UNCOV
52
        var (
×
UNCOV
53
                lockFileHandle *os.File
×
UNCOV
54
                err            error
×
UNCOV
55
        )
×
UNCOV
56
        for {
×
UNCOV
57
                // Attempt to acquire the lock file. If it already exists, wait
×
UNCOV
58
                // for a bit and retry.
×
UNCOV
59
                lockFileHandle, err = os.OpenFile(
×
UNCOV
60
                        lockFile, os.O_CREATE|os.O_EXCL, 0600,
×
UNCOV
61
                )
×
UNCOV
62
                if err == nil {
×
UNCOV
63
                        // Lock acquired.
×
UNCOV
64
                        break
×
65
                }
66

67
                // Wait for a bit and retry.
68
                select {
×
69
                case <-timeout:
×
70
                        panic("timeout waiting for lock file")
×
71
                case <-time.After(10 * time.Millisecond):
×
72
                }
73
        }
74

75
        // Release the lock file when we're done.
UNCOV
76
        defer func() {
×
UNCOV
77
                // Always close file first, Windows won't allow us to remove it
×
UNCOV
78
                // otherwise.
×
UNCOV
79
                _ = lockFileHandle.Close()
×
UNCOV
80
                err := os.Remove(lockFile)
×
UNCOV
81
                if err != nil {
×
82
                        panic(fmt.Errorf("couldn't remove lock file: %w", err))
×
83
                }
84
        }()
85

UNCOV
86
        portFile := filepath.Join(os.TempDir(), uniquePortFile)
×
UNCOV
87
        port, err := os.ReadFile(portFile)
×
UNCOV
88
        if err != nil {
×
89
                if !os.IsNotExist(err) {
×
90
                        panic(fmt.Errorf("error reading port file: %w", err))
×
91
                }
92
                port = []byte(strconv.Itoa(defaultNodePort))
×
93
        }
94

UNCOV
95
        lastPort, err := strconv.Atoi(string(port))
×
UNCOV
96
        if err != nil {
×
97
                panic(fmt.Errorf("error parsing port: %w", err))
×
98
        }
99

100
        // We take the next one.
UNCOV
101
        lastPort++
×
UNCOV
102
        for lastPort < 65535 {
×
UNCOV
103
                // If there are no errors while attempting to listen on this
×
UNCOV
104
                // port, close the socket and return it as available. While it
×
UNCOV
105
                // could be the case that some other process picks up this port
×
UNCOV
106
                // between the time the socket is closed, and it's reopened in
×
UNCOV
107
                // the harness node, in practice in CI servers this seems much
×
UNCOV
108
                // less likely than simply some other process already being
×
UNCOV
109
                // bound at the start of the tests.
×
UNCOV
110
                addr := fmt.Sprintf(ListenerFormat, lastPort)
×
UNCOV
111
                l, err := net.Listen("tcp4", addr)
×
UNCOV
112
                if err == nil {
×
UNCOV
113
                        err := l.Close()
×
UNCOV
114
                        if err == nil {
×
UNCOV
115
                                err := os.WriteFile(
×
UNCOV
116
                                        portFile,
×
UNCOV
117
                                        []byte(strconv.Itoa(lastPort)), 0600,
×
UNCOV
118
                                )
×
UNCOV
119
                                if err != nil {
×
120
                                        panic(fmt.Errorf("error updating "+
×
121
                                                "port file: %w", err))
×
122
                                }
123

UNCOV
124
                                return lastPort
×
125
                        }
126
                }
127
                lastPort++
×
128

×
129
                // Start from the beginning if we reached the end of the port
×
130
                // range. We need to do this because the lock file now is
×
131
                // persistent across runs on the same machine during the same
×
132
                // boot/uptime cycle. So in order to make this work on
×
133
                // developer's machines, we need to reset the port to the
×
134
                // default value when we reach the end of the range.
×
135
                if lastPort == 65535 {
×
136
                        lastPort = defaultNodePort
×
137
                }
×
138
        }
139

140
        // No ports available? Must be a mistake.
141
        panic("no ports available for listening")
×
142
}
143

144
// GenerateSystemUniqueListenerAddresses is a function that returns two
145
// listener addresses with unique ports per system and should be used to
146
// overwrite rpctest's default generator which is prone to use colliding ports.
UNCOV
147
func GenerateSystemUniqueListenerAddresses() (string, string) {
×
UNCOV
148
        port1 := NextAvailablePort()
×
UNCOV
149
        port2 := NextAvailablePort()
×
UNCOV
150
        return fmt.Sprintf(ListenerFormat, port1),
×
UNCOV
151
                fmt.Sprintf(ListenerFormat, port2)
×
UNCOV
152
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc