Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"time" |
| 5 | 5 |
|
| 6 | 6 |
dockerspec "github.com/moby/docker-image-spec/specs-go/v1" |
| 7 |
+ "github.com/moby/moby/api/types/network" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
// MinimumDuration puts a minimum on user configured duration. |
| ... | ... |
@@ -28,7 +29,7 @@ type Config struct {
|
| 28 | 28 |
AttachStdin bool // Attach the standard input, makes possible user interaction |
| 29 | 29 |
AttachStdout bool // Attach the standard output |
| 30 | 30 |
AttachStderr bool // Attach the standard error |
| 31 |
- ExposedPorts PortSet `json:",omitempty"` // List of exposed ports |
|
| 31 |
+ ExposedPorts network.PortSet `json:",omitempty"` // List of exposed ports |
|
| 32 | 32 |
Tty bool // Attach standard streams to a tty, including stdin if it is not closed. |
| 33 | 33 |
OpenStdin bool // Open stdin |
| 34 | 34 |
StdinOnce bool // If true, close stdin after the 1 attached client disconnects. |
| ... | ... |
@@ -421,7 +421,7 @@ type HostConfig struct {
|
| 421 | 421 |
ContainerIDFile string // File (path) where the containerId is written |
| 422 | 422 |
LogConfig LogConfig // Configuration of the logs for this container |
| 423 | 423 |
NetworkMode NetworkMode // Network mode to use for the container |
| 424 |
- PortBindings PortMap // Port mapping between the exposed port (container) and the host |
|
| 424 |
+ PortBindings network.PortMap // Port mapping between the exposed port (container) and the host |
|
| 425 | 425 |
RestartPolicy RestartPolicy // Restart policy to be used for the container |
| 426 | 426 |
AutoRemove bool // Automatically remove container when it exits |
| 427 | 427 |
VolumeDriver string // Name of the volume driver used to mount volumes |
| 428 | 428 |
deleted file mode 100644 |
| ... | ... |
@@ -1,346 +0,0 @@ |
| 1 |
-package container |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "iter" |
|
| 7 |
- "net/netip" |
|
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 |
- "unique" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-// NetworkProtocol represents a network protocol for a port. |
|
| 14 |
-type NetworkProtocol string |
|
| 15 |
- |
|
| 16 |
-const ( |
|
| 17 |
- TCP NetworkProtocol = "tcp" |
|
| 18 |
- UDP NetworkProtocol = "udp" |
|
| 19 |
- SCTP NetworkProtocol = "sctp" |
|
| 20 |
-) |
|
| 21 |
- |
|
| 22 |
-// Sentinel port proto value for zero Port and PortRange values. |
|
| 23 |
-var protoZero unique.Handle[NetworkProtocol] |
|
| 24 |
- |
|
| 25 |
-// Port is a type representing a single port number and protocol in the format "<portnum>/[<proto>]". |
|
| 26 |
-// |
|
| 27 |
-// The zero port value, i.e. Port{}, is invalid; use [ParsePort] to create a valid Port value.
|
|
| 28 |
-type Port struct {
|
|
| 29 |
- num uint16 |
|
| 30 |
- proto unique.Handle[NetworkProtocol] |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-// ParsePort parses s as a [Port]. |
|
| 34 |
-// |
|
| 35 |
-// It normalizes the provided protocol such that "80/tcp", "80/TCP", and "80/tCp" are equivalent. |
|
| 36 |
-// If a port number is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 37 |
-func ParsePort(s string) (Port, error) {
|
|
| 38 |
- if s == "" {
|
|
| 39 |
- return Port{}, errors.New("invalid port: value is empty")
|
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- port, proto, _ := strings.Cut(s, "/") |
|
| 43 |
- |
|
| 44 |
- portNum, err := parsePortNumber(port) |
|
| 45 |
- if err != nil {
|
|
| 46 |
- return Port{}, fmt.Errorf("invalid port '%s': %w", port, err)
|
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- normalizedPortProto := normalizePortProto(proto) |
|
| 50 |
- return Port{num: portNum, proto: normalizedPortProto}, nil
|
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// MustParsePort calls [ParsePort](s) and panics on error. |
|
| 54 |
-// |
|
| 55 |
-// It is intended for use in tests with hard-coded strings. |
|
| 56 |
-func MustParsePort(s string) Port {
|
|
| 57 |
- p, err := ParsePort(s) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- panic(err) |
|
| 60 |
- } |
|
| 61 |
- return p |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-// PortFrom returns a [Port] with the given number and protocol. |
|
| 65 |
-// |
|
| 66 |
-// If no protocol is specified (i.e. proto == ""), then PortFrom returns Port{}, false.
|
|
| 67 |
-func PortFrom(num uint16, proto NetworkProtocol) (p Port, ok bool) {
|
|
| 68 |
- if proto == "" {
|
|
| 69 |
- return Port{}, false
|
|
| 70 |
- } |
|
| 71 |
- normalized := normalizePortProto(string(proto)) |
|
| 72 |
- return Port{num: num, proto: normalized}, true
|
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-// Num returns p's port number. |
|
| 76 |
-func (p Port) Num() uint16 {
|
|
| 77 |
- return p.num |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Proto returns p's network protocol. |
|
| 81 |
-func (p Port) Proto() NetworkProtocol {
|
|
| 82 |
- return p.proto.Value() |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-// IsZero reports whether p is the zero value. |
|
| 86 |
-func (p Port) IsZero() bool {
|
|
| 87 |
- return p.proto == protoZero |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-// IsValid reports whether p is an initialized valid port (not the zero value). |
|
| 91 |
-func (p Port) IsValid() bool {
|
|
| 92 |
- return p.proto != protoZero |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-// String returns a string representation of the port in the format "<portnum>/<proto>". |
|
| 96 |
-// If the port is the zero value, it returns "invalid port". |
|
| 97 |
-func (p Port) String() string {
|
|
| 98 |
- switch p.proto {
|
|
| 99 |
- case protoZero: |
|
| 100 |
- return "invalid port" |
|
| 101 |
- default: |
|
| 102 |
- return string(p.AppendTo(nil)) |
|
| 103 |
- } |
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-// AppendText implements [encoding.TextAppender] interface. |
|
| 107 |
-// It is the same as [Port.AppendTo] but returns an error to satisfy the interface. |
|
| 108 |
-func (p Port) AppendText(b []byte) ([]byte, error) {
|
|
| 109 |
- return p.AppendTo(b), nil |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-// AppendTo appends a text encoding of p to b and returns the extended buffer. |
|
| 113 |
-func (p Port) AppendTo(b []byte) []byte {
|
|
| 114 |
- if p.IsZero() {
|
|
| 115 |
- return b |
|
| 116 |
- } |
|
| 117 |
- return fmt.Appendf(b, "%d/%s", p.num, p.proto.Value()) |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 121 |
-func (p Port) MarshalText() ([]byte, error) {
|
|
| 122 |
- return p.AppendText(nil) |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 126 |
-func (p *Port) UnmarshalText(text []byte) error {
|
|
| 127 |
- if len(text) == 0 {
|
|
| 128 |
- *p = Port{}
|
|
| 129 |
- return nil |
|
| 130 |
- } |
|
| 131 |
- |
|
| 132 |
- port, err := ParsePort(string(text)) |
|
| 133 |
- if err != nil {
|
|
| 134 |
- return err |
|
| 135 |
- } |
|
| 136 |
- |
|
| 137 |
- *p = port |
|
| 138 |
- return nil |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-// Range returns a [PortRange] representing the single port. |
|
| 142 |
-func (p Port) Range() PortRange {
|
|
| 143 |
- return PortRange{start: p.num, end: p.num, proto: p.proto}
|
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-// PortSet is a collection of structs indexed by [Port]. |
|
| 147 |
-type PortSet = map[Port]struct{}
|
|
| 148 |
- |
|
| 149 |
-// PortBinding represents a binding between a Host IP address and a Host Port. |
|
| 150 |
-type PortBinding struct {
|
|
| 151 |
- // HostIP is the host IP Address |
|
| 152 |
- HostIP netip.Addr `json:"HostIp"` |
|
| 153 |
- // HostPort is the host port number |
|
| 154 |
- HostPort string `json:"HostPort"` |
|
| 155 |
-} |
|
| 156 |
- |
|
| 157 |
-// PortMap is a collection of [PortBinding] indexed by [Port]. |
|
| 158 |
-type PortMap = map[Port][]PortBinding |
|
| 159 |
- |
|
| 160 |
-// PortRange represents a range of port numbers and a protocol in the format "8000-9000/tcp". |
|
| 161 |
-// |
|
| 162 |
-// The zero port range value, i.e. PortRange{}, is invalid; use [ParsePortRange] to create a valid PortRange value.
|
|
| 163 |
-type PortRange struct {
|
|
| 164 |
- start uint16 |
|
| 165 |
- end uint16 |
|
| 166 |
- proto unique.Handle[NetworkProtocol] |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-// ParsePortRange parses s as a [PortRange]. |
|
| 170 |
-// |
|
| 171 |
-// It normalizes the provided protocol such that "80-90/tcp", "80-90/TCP", and "80-90/tCp" are equivalent. |
|
| 172 |
-// If a port number range is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 173 |
-func ParsePortRange(s string) (PortRange, error) {
|
|
| 174 |
- if s == "" {
|
|
| 175 |
- return PortRange{}, errors.New("invalid port range: value is empty")
|
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- portRange, proto, _ := strings.Cut(s, "/") |
|
| 179 |
- |
|
| 180 |
- start, end, ok := strings.Cut(portRange, "-") |
|
| 181 |
- startVal, err := parsePortNumber(start) |
|
| 182 |
- if err != nil {
|
|
| 183 |
- return PortRange{}, fmt.Errorf("invalid start port '%s': %w", start, err)
|
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- portProto := normalizePortProto(proto) |
|
| 187 |
- |
|
| 188 |
- if !ok || start == end {
|
|
| 189 |
- return PortRange{start: startVal, end: startVal, proto: portProto}, nil
|
|
| 190 |
- } |
|
| 191 |
- |
|
| 192 |
- endVal, err := parsePortNumber(end) |
|
| 193 |
- if err != nil {
|
|
| 194 |
- return PortRange{}, fmt.Errorf("invalid end port '%s': %w", end, err)
|
|
| 195 |
- } |
|
| 196 |
- if endVal < startVal {
|
|
| 197 |
- return PortRange{}, errors.New("invalid port range: " + s)
|
|
| 198 |
- } |
|
| 199 |
- return PortRange{start: startVal, end: endVal, proto: portProto}, nil
|
|
| 200 |
-} |
|
| 201 |
- |
|
| 202 |
-// MustParsePortRange calls [ParsePortRange](s) and panics on error. |
|
| 203 |
-// It is intended for use in tests with hard-coded strings. |
|
| 204 |
-func MustParsePortRange(s string) PortRange {
|
|
| 205 |
- pr, err := ParsePortRange(s) |
|
| 206 |
- if err != nil {
|
|
| 207 |
- panic(err) |
|
| 208 |
- } |
|
| 209 |
- return pr |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-// PortRangeFrom returns a [PortRange] with the given start and end port numbers and protocol. |
|
| 213 |
-// |
|
| 214 |
-// If end < start or no protocol is specified (i.e. proto == ""), then PortRangeFrom returns PortRange{}, false.
|
|
| 215 |
-func PortRangeFrom(start, end uint16, proto NetworkProtocol) (pr PortRange, ok bool) {
|
|
| 216 |
- if end < start || proto == "" {
|
|
| 217 |
- return PortRange{}, false
|
|
| 218 |
- } |
|
| 219 |
- normalized := normalizePortProto(string(proto)) |
|
| 220 |
- return PortRange{start: start, end: end, proto: normalized}, true
|
|
| 221 |
-} |
|
| 222 |
- |
|
| 223 |
-// Start returns pr's start port number. |
|
| 224 |
-func (pr PortRange) Start() uint16 {
|
|
| 225 |
- return pr.start |
|
| 226 |
-} |
|
| 227 |
- |
|
| 228 |
-// End returns pr's end port number. |
|
| 229 |
-func (pr PortRange) End() uint16 {
|
|
| 230 |
- return pr.end |
|
| 231 |
-} |
|
| 232 |
- |
|
| 233 |
-// Proto returns pr's network protocol. |
|
| 234 |
-func (pr PortRange) Proto() NetworkProtocol {
|
|
| 235 |
- return pr.proto.Value() |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-// IsZero reports whether pr is the zero value. |
|
| 239 |
-func (pr PortRange) IsZero() bool {
|
|
| 240 |
- return pr.proto == protoZero |
|
| 241 |
-} |
|
| 242 |
- |
|
| 243 |
-// IsValid reports whether pr is an initialized valid port range (not the zero value). |
|
| 244 |
-func (pr PortRange) IsValid() bool {
|
|
| 245 |
- return pr.proto != protoZero |
|
| 246 |
-} |
|
| 247 |
- |
|
| 248 |
-// String returns a string representation of the port range in the format "<start>-<end>/<proto>" or "<portnum>/<proto>" if start == end. |
|
| 249 |
-// If the port range is the zero value, it returns "invalid port range". |
|
| 250 |
-func (pr PortRange) String() string {
|
|
| 251 |
- switch pr.proto {
|
|
| 252 |
- case protoZero: |
|
| 253 |
- return "invalid port range" |
|
| 254 |
- default: |
|
| 255 |
- return string(pr.AppendTo(nil)) |
|
| 256 |
- } |
|
| 257 |
-} |
|
| 258 |
- |
|
| 259 |
-// AppendText implements [encoding.TextAppender] interface. |
|
| 260 |
-// It is the same as [PortRange.AppendTo] but returns an error to satisfy the interface. |
|
| 261 |
-func (pr PortRange) AppendText(b []byte) ([]byte, error) {
|
|
| 262 |
- return pr.AppendTo(b), nil |
|
| 263 |
-} |
|
| 264 |
- |
|
| 265 |
-// AppendTo appends a text encoding of pr to b and returns the extended buffer. |
|
| 266 |
-func (pr PortRange) AppendTo(b []byte) []byte {
|
|
| 267 |
- if pr.IsZero() {
|
|
| 268 |
- return b |
|
| 269 |
- } |
|
| 270 |
- if pr.start == pr.end {
|
|
| 271 |
- return fmt.Appendf(b, "%d/%s", pr.start, pr.proto.Value()) |
|
| 272 |
- } |
|
| 273 |
- return fmt.Appendf(b, "%d-%d/%s", pr.start, pr.end, pr.proto.Value()) |
|
| 274 |
-} |
|
| 275 |
- |
|
| 276 |
-// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 277 |
-func (pr PortRange) MarshalText() ([]byte, error) {
|
|
| 278 |
- return pr.AppendText(nil) |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 282 |
-func (pr *PortRange) UnmarshalText(text []byte) error {
|
|
| 283 |
- if len(text) == 0 {
|
|
| 284 |
- *pr = PortRange{}
|
|
| 285 |
- return nil |
|
| 286 |
- } |
|
| 287 |
- |
|
| 288 |
- portRange, err := ParsePortRange(string(text)) |
|
| 289 |
- if err != nil {
|
|
| 290 |
- return err |
|
| 291 |
- } |
|
| 292 |
- *pr = portRange |
|
| 293 |
- return nil |
|
| 294 |
-} |
|
| 295 |
- |
|
| 296 |
-// Range returns pr. |
|
| 297 |
-func (pr PortRange) Range() PortRange {
|
|
| 298 |
- return pr |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-// All returns an iterator over all the individual ports in the range. |
|
| 302 |
-// |
|
| 303 |
-// For example: |
|
| 304 |
-// |
|
| 305 |
-// for port := range pr.All() {
|
|
| 306 |
-// // ... |
|
| 307 |
-// } |
|
| 308 |
-func (pr PortRange) All() iter.Seq[Port] {
|
|
| 309 |
- return func(yield func(Port) bool) {
|
|
| 310 |
- for i := uint32(pr.Start()); i <= uint32(pr.End()); i++ {
|
|
| 311 |
- if !yield(Port{num: uint16(i), proto: pr.proto}) {
|
|
| 312 |
- return |
|
| 313 |
- } |
|
| 314 |
- } |
|
| 315 |
- } |
|
| 316 |
-} |
|
| 317 |
- |
|
| 318 |
-// parsePortNumber parses rawPort into an int, unwrapping strconv errors |
|
| 319 |
-// and returning a single "out of range" error for any value outside 0–65535. |
|
| 320 |
-func parsePortNumber(rawPort string) (uint16, error) {
|
|
| 321 |
- if rawPort == "" {
|
|
| 322 |
- return 0, errors.New("value is empty")
|
|
| 323 |
- } |
|
| 324 |
- port, err := strconv.ParseUint(rawPort, 10, 16) |
|
| 325 |
- if err != nil {
|
|
| 326 |
- var numErr *strconv.NumError |
|
| 327 |
- if errors.As(err, &numErr) {
|
|
| 328 |
- err = numErr.Err |
|
| 329 |
- } |
|
| 330 |
- return 0, err |
|
| 331 |
- } |
|
| 332 |
- |
|
| 333 |
- return uint16(port), nil |
|
| 334 |
-} |
|
| 335 |
- |
|
| 336 |
-// normalizePortProto normalizes the protocol string such that "tcp", "TCP", and "tCp" are equivalent. |
|
| 337 |
-// If proto is not specified, it defaults to "tcp". |
|
| 338 |
-func normalizePortProto(proto string) unique.Handle[NetworkProtocol] {
|
|
| 339 |
- if proto == "" {
|
|
| 340 |
- return unique.Make(TCP) |
|
| 341 |
- } |
|
| 342 |
- |
|
| 343 |
- proto = strings.ToLower(proto) |
|
| 344 |
- |
|
| 345 |
- return unique.Make(NetworkProtocol(proto)) |
|
| 346 |
-} |
| ... | ... |
@@ -6,10 +6,13 @@ import ( |
| 6 | 6 |
|
| 7 | 7 |
// NetworkSettings exposes the network settings in the api |
| 8 | 8 |
type NetworkSettings struct {
|
| 9 |
- SandboxID string // SandboxID uniquely represents a container's network stack |
|
| 10 |
- SandboxKey string // SandboxKey identifies the sandbox |
|
| 11 |
- Ports PortMap // Ports is a collection of PortBinding indexed by Port |
|
| 12 |
- Networks map[string]*network.EndpointSettings |
|
| 9 |
+ SandboxID string // SandboxID uniquely represents a container's network stack |
|
| 10 |
+ SandboxKey string // SandboxKey identifies the sandbox |
|
| 11 |
+ |
|
| 12 |
+ // Ports is a collection of [network.PortBinding] indexed by [network.Port] |
|
| 13 |
+ Ports network.PortMap |
|
| 14 |
+ |
|
| 15 |
+ Networks map[string]*network.EndpointSettings |
|
| 13 | 16 |
} |
| 14 | 17 |
|
| 15 | 18 |
// NetworkSettingsSummary provides a summary of container's networks |
| 16 | 19 |
deleted file mode 100644 |
| ... | ... |
@@ -1,600 +0,0 @@ |
| 1 |
-package container |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "reflect" |
|
| 7 |
- "slices" |
|
| 8 |
- "strings" |
|
| 9 |
- "testing" |
|
| 10 |
- |
|
| 11 |
- "gotest.tools/v3/assert" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-type TestRanger interface {
|
|
| 15 |
- Range() PortRange |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-var _ TestRanger = Port{}
|
|
| 19 |
-var _ TestRanger = PortRange{}
|
|
| 20 |
- |
|
| 21 |
-func TestPort(t *testing.T) {
|
|
| 22 |
- t.Run("Zero Value", func(t *testing.T) {
|
|
| 23 |
- var p Port |
|
| 24 |
- assert.Check(t, p.IsZero()) |
|
| 25 |
- assert.Check(t, !p.IsValid()) |
|
| 26 |
- assert.Equal(t, p.String(), "invalid port") |
|
| 27 |
- |
|
| 28 |
- t.Run("Marshal Unmarshal", func(t *testing.T) {
|
|
| 29 |
- var p Port |
|
| 30 |
- bytes, err := p.MarshalText() |
|
| 31 |
- assert.NilError(t, err) |
|
| 32 |
- assert.Check(t, len(bytes) == 0) |
|
| 33 |
- |
|
| 34 |
- err = p.UnmarshalText([]byte(""))
|
|
| 35 |
- assert.NilError(t, err) |
|
| 36 |
- assert.Equal(t, p, Port{})
|
|
| 37 |
- }) |
|
| 38 |
- |
|
| 39 |
- t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
|
|
| 40 |
- var p Port |
|
| 41 |
- bytes, err := json.Marshal(p) |
|
| 42 |
- assert.NilError(t, err) |
|
| 43 |
- assert.Equal(t, string(bytes), `""`) |
|
| 44 |
- |
|
| 45 |
- err = json.Unmarshal([]byte(`""`), &p) |
|
| 46 |
- assert.NilError(t, err) |
|
| 47 |
- assert.Equal(t, p, Port{})
|
|
| 48 |
- }) |
|
| 49 |
- }) |
|
| 50 |
- |
|
| 51 |
- t.Run("PortFrom", func(t *testing.T) {
|
|
| 52 |
- tests := []struct {
|
|
| 53 |
- num uint16 |
|
| 54 |
- proto NetworkProtocol |
|
| 55 |
- }{
|
|
| 56 |
- {0, TCP},
|
|
| 57 |
- {80, TCP},
|
|
| 58 |
- {8080, TCP},
|
|
| 59 |
- {65535, TCP},
|
|
| 60 |
- {80, UDP},
|
|
| 61 |
- {8080, SCTP},
|
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- for _, tc := range tests {
|
|
| 65 |
- t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
|
|
| 66 |
- p, ok := PortFrom(tc.num, tc.proto) |
|
| 67 |
- assert.Check(t, ok) |
|
| 68 |
- assert.Equal(t, p.Num(), tc.num) |
|
| 69 |
- assert.Equal(t, p.Proto(), tc.proto) |
|
| 70 |
- }) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 74 |
- pr1 := portFrom(1234, "tcp") |
|
| 75 |
- pr2 := portFrom(1234, "TCP") |
|
| 76 |
- pr3 := portFrom(1234, "tCp") |
|
| 77 |
- assert.Equal(t, pr1, pr2) |
|
| 78 |
- assert.Equal(t, pr2, pr3) |
|
| 79 |
- }) |
|
| 80 |
- |
|
| 81 |
- negativeTests := []struct {
|
|
| 82 |
- num uint16 |
|
| 83 |
- proto NetworkProtocol |
|
| 84 |
- }{
|
|
| 85 |
- {0, ""},
|
|
| 86 |
- {80, ""},
|
|
| 87 |
- } |
|
| 88 |
- for _, tc := range negativeTests {
|
|
| 89 |
- t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
|
|
| 90 |
- p, ok := PortFrom(tc.num, tc.proto) |
|
| 91 |
- assert.Check(t, !ok) |
|
| 92 |
- assert.Check(t, p.IsZero()) |
|
| 93 |
- assert.Check(t, !p.IsValid()) |
|
| 94 |
- assert.Equal(t, p.String(), "invalid port") |
|
| 95 |
- }) |
|
| 96 |
- } |
|
| 97 |
- }) |
|
| 98 |
- |
|
| 99 |
- t.Run("ParsePort", func(t *testing.T) {
|
|
| 100 |
- tests := []struct {
|
|
| 101 |
- in string |
|
| 102 |
- port Port // output of ParsePort() |
|
| 103 |
- str string // output of String(). |
|
| 104 |
- portRange PortRange // output of Range() |
|
| 105 |
- }{
|
|
| 106 |
- // Zero port |
|
| 107 |
- {
|
|
| 108 |
- in: "0/tcp", |
|
| 109 |
- port: portFrom(0, TCP), |
|
| 110 |
- str: "0/tcp", |
|
| 111 |
- portRange: portRangeFrom(0, 0, TCP), |
|
| 112 |
- }, |
|
| 113 |
- // Max valid port |
|
| 114 |
- {
|
|
| 115 |
- in: "65535/tcp", |
|
| 116 |
- port: portFrom(65535, TCP), |
|
| 117 |
- str: "65535/tcp", |
|
| 118 |
- portRange: portRangeFrom(65535, 65535, TCP), |
|
| 119 |
- }, |
|
| 120 |
- // Simple valid ports |
|
| 121 |
- {
|
|
| 122 |
- in: "1234/tcp", |
|
| 123 |
- port: portFrom(1234, TCP), |
|
| 124 |
- str: "1234/tcp", |
|
| 125 |
- portRange: portRangeFrom(1234, 1234, TCP), |
|
| 126 |
- }, |
|
| 127 |
- {
|
|
| 128 |
- in: "1234/udp", |
|
| 129 |
- port: portFrom(1234, UDP), |
|
| 130 |
- str: "1234/udp", |
|
| 131 |
- portRange: portRangeFrom(1234, 1234, UDP), |
|
| 132 |
- }, |
|
| 133 |
- {
|
|
| 134 |
- in: "1234/sctp", |
|
| 135 |
- port: portFrom(1234, SCTP), |
|
| 136 |
- str: "1234/sctp", |
|
| 137 |
- portRange: portRangeFrom(1234, 1234, SCTP), |
|
| 138 |
- }, |
|
| 139 |
- // Default protocol is tcp |
|
| 140 |
- {
|
|
| 141 |
- in: "1234", |
|
| 142 |
- port: portFrom(1234, TCP), |
|
| 143 |
- str: "1234/tcp", |
|
| 144 |
- portRange: portRangeFrom(1234, 1234, TCP), |
|
| 145 |
- }, |
|
| 146 |
- // Default protocol is tcp |
|
| 147 |
- {
|
|
| 148 |
- in: "1234/", |
|
| 149 |
- port: portFrom(1234, TCP), |
|
| 150 |
- str: "1234/tcp", |
|
| 151 |
- portRange: portRangeFrom(1234, 1234, TCP), |
|
| 152 |
- }, |
|
| 153 |
- {
|
|
| 154 |
- in: "1234/tcp:ipv6only", |
|
| 155 |
- port: portFrom(1234, "tcp:ipv6only"), |
|
| 156 |
- str: "1234/tcp:ipv6only", |
|
| 157 |
- portRange: portRangeFrom(1234, 1234, "tcp:ipv6only"), |
|
| 158 |
- }, |
|
| 159 |
- } |
|
| 160 |
- |
|
| 161 |
- for _, tc := range tests {
|
|
| 162 |
- t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
|
|
| 163 |
- got, err := ParsePort(tc.in) |
|
| 164 |
- assert.NilError(t, err) |
|
| 165 |
- assert.Equal(t, got, tc.port) |
|
| 166 |
- |
|
| 167 |
- MustParsePort(tc.in) // should not panic |
|
| 168 |
- |
|
| 169 |
- assert.Check(t, !got.IsZero()) |
|
| 170 |
- assert.Check(t, got.IsValid()) |
|
| 171 |
- |
|
| 172 |
- // Check that ParsePort is a pure function. |
|
| 173 |
- got2, err := ParsePort(tc.in) |
|
| 174 |
- assert.NilError(t, err) |
|
| 175 |
- assert.Equal(t, got2, got) |
|
| 176 |
- |
|
| 177 |
- // Check that ParsePort(port.String()) is the identity function. |
|
| 178 |
- got3, err := ParsePort(got.String()) |
|
| 179 |
- assert.NilError(t, err) |
|
| 180 |
- assert.Equal(t, got3, got) |
|
| 181 |
- |
|
| 182 |
- // Check String() output |
|
| 183 |
- s := got.String() |
|
| 184 |
- wants := tc.str |
|
| 185 |
- if wants == "" {
|
|
| 186 |
- wants = tc.in |
|
| 187 |
- } |
|
| 188 |
- assert.Equal(t, s, wants) |
|
| 189 |
- |
|
| 190 |
- js := `"` + tc.in + `"` |
|
| 191 |
- var jsgot Port |
|
| 192 |
- err = json.Unmarshal([]byte(js), &jsgot) |
|
| 193 |
- assert.NilError(t, err) |
|
| 194 |
- assert.Equal(t, jsgot, got) |
|
| 195 |
- |
|
| 196 |
- jsb, err := json.Marshal(jsgot) |
|
| 197 |
- assert.NilError(t, err) |
|
| 198 |
- |
|
| 199 |
- jswant := `"` + wants + `"` |
|
| 200 |
- assert.Equal(t, string(jsb), jswant) |
|
| 201 |
- |
|
| 202 |
- // Check Range() output |
|
| 203 |
- r := got.Range() |
|
| 204 |
- assert.Equal(t, r, tc.portRange) |
|
| 205 |
- }) |
|
| 206 |
- } |
|
| 207 |
- |
|
| 208 |
- t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 209 |
- p1 := MustParsePort("1234/tcp")
|
|
| 210 |
- p2 := MustParsePort("1234/TCP")
|
|
| 211 |
- p3 := MustParsePort("1234/tCp")
|
|
| 212 |
- assert.Equal(t, p1, p2) |
|
| 213 |
- assert.Equal(t, p2, p3) |
|
| 214 |
- }) |
|
| 215 |
- |
|
| 216 |
- negativeTests := []string{
|
|
| 217 |
- // Empty string |
|
| 218 |
- "", |
|
| 219 |
- // Whitespace-only string |
|
| 220 |
- " ", |
|
| 221 |
- // No port number |
|
| 222 |
- "/", |
|
| 223 |
- // No port number (protocol only) |
|
| 224 |
- "/tcp", |
|
| 225 |
- // Negative port |
|
| 226 |
- "-1", |
|
| 227 |
- // Too large port |
|
| 228 |
- "65536", |
|
| 229 |
- // Non-numeric port |
|
| 230 |
- "foo", |
|
| 231 |
- // Port range instead of single port |
|
| 232 |
- "1234-1240/udp", |
|
| 233 |
- // Port range instead of single port without protocol |
|
| 234 |
- "1234-1240", |
|
| 235 |
- // Garbage port |
|
| 236 |
- "asd1234/tcp", |
|
| 237 |
- } |
|
| 238 |
- |
|
| 239 |
- for _, s := range negativeTests {
|
|
| 240 |
- t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
|
|
| 241 |
- got, err := ParsePort(s) |
|
| 242 |
- assert.ErrorContains(t, err, "invalid port") |
|
| 243 |
- assert.Check(t, got.IsZero()) |
|
| 244 |
- assert.Check(t, !got.IsValid()) |
|
| 245 |
- |
|
| 246 |
- // Skip JSON unmarshalling test for empty string as that should succeed. |
|
| 247 |
- // See test "Zero Value" above. |
|
| 248 |
- if s == "" {
|
|
| 249 |
- return |
|
| 250 |
- } |
|
| 251 |
- |
|
| 252 |
- var jsgot Port |
|
| 253 |
- js := []byte(`"` + s + `"`) |
|
| 254 |
- err = json.Unmarshal(js, &jsgot) |
|
| 255 |
- assert.ErrorContains(t, err, "invalid port") |
|
| 256 |
- assert.Equal(t, jsgot, Port{})
|
|
| 257 |
- }) |
|
| 258 |
- } |
|
| 259 |
- }) |
|
| 260 |
-} |
|
| 261 |
- |
|
| 262 |
-func TestPortRange(t *testing.T) {
|
|
| 263 |
- t.Run("Zero Value", func(t *testing.T) {
|
|
| 264 |
- var pr PortRange |
|
| 265 |
- assert.Check(t, pr.IsZero()) |
|
| 266 |
- assert.Check(t, !pr.IsValid()) |
|
| 267 |
- assert.Equal(t, pr.String(), "invalid port range") |
|
| 268 |
- |
|
| 269 |
- t.Run("Marshal Unmarshal", func(t *testing.T) {
|
|
| 270 |
- var pr PortRange |
|
| 271 |
- bytes, err := pr.MarshalText() |
|
| 272 |
- assert.NilError(t, err) |
|
| 273 |
- assert.Check(t, len(bytes) == 0) |
|
| 274 |
- |
|
| 275 |
- err = pr.UnmarshalText([]byte(""))
|
|
| 276 |
- assert.NilError(t, err) |
|
| 277 |
- assert.Equal(t, pr, PortRange{})
|
|
| 278 |
- }) |
|
| 279 |
- |
|
| 280 |
- t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
|
|
| 281 |
- var pr PortRange |
|
| 282 |
- bytes, err := json.Marshal(pr) |
|
| 283 |
- assert.NilError(t, err) |
|
| 284 |
- assert.Equal(t, string(bytes), `""`) |
|
| 285 |
- |
|
| 286 |
- err = json.Unmarshal([]byte(`""`), &pr) |
|
| 287 |
- assert.NilError(t, err) |
|
| 288 |
- assert.Equal(t, pr, PortRange{})
|
|
| 289 |
- }) |
|
| 290 |
- }) |
|
| 291 |
- |
|
| 292 |
- t.Run("PortRangeFrom", func(t *testing.T) {
|
|
| 293 |
- tests := []struct {
|
|
| 294 |
- start uint16 |
|
| 295 |
- end uint16 |
|
| 296 |
- proto NetworkProtocol |
|
| 297 |
- }{
|
|
| 298 |
- {0, 0, TCP},
|
|
| 299 |
- {0, 1234, TCP},
|
|
| 300 |
- {80, 80, TCP},
|
|
| 301 |
- {80, 8080, TCP},
|
|
| 302 |
- {1234, 65535, TCP},
|
|
| 303 |
- {80, 80, UDP},
|
|
| 304 |
- {80, 8080, SCTP},
|
|
| 305 |
- } |
|
| 306 |
- |
|
| 307 |
- for _, tc := range tests {
|
|
| 308 |
- t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
|
|
| 309 |
- pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto) |
|
| 310 |
- assert.Check(t, ok) |
|
| 311 |
- assert.Equal(t, pr.Start(), tc.start) |
|
| 312 |
- assert.Equal(t, pr.End(), tc.end) |
|
| 313 |
- assert.Equal(t, pr.Proto(), tc.proto) |
|
| 314 |
- }) |
|
| 315 |
- } |
|
| 316 |
- |
|
| 317 |
- t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 318 |
- pr1, _ := PortRangeFrom(1234, 5678, "tcp") |
|
| 319 |
- pr2, _ := PortRangeFrom(1234, 5678, "TCP") |
|
| 320 |
- pr3, _ := PortRangeFrom(1234, 5678, "tCp") |
|
| 321 |
- assert.Equal(t, pr1, pr2) |
|
| 322 |
- assert.Equal(t, pr2, pr3) |
|
| 323 |
- }) |
|
| 324 |
- |
|
| 325 |
- negativeTests := []struct {
|
|
| 326 |
- start uint16 |
|
| 327 |
- end uint16 |
|
| 328 |
- proto NetworkProtocol |
|
| 329 |
- }{
|
|
| 330 |
- {1234, 80, TCP}, // end < start
|
|
| 331 |
- {0, 0, ""}, // empty protocol
|
|
| 332 |
- } |
|
| 333 |
- for _, tc := range negativeTests {
|
|
| 334 |
- t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
|
|
| 335 |
- pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto) |
|
| 336 |
- assert.Check(t, !ok) |
|
| 337 |
- assert.Check(t, pr.IsZero()) |
|
| 338 |
- assert.Check(t, !pr.IsValid()) |
|
| 339 |
- }) |
|
| 340 |
- } |
|
| 341 |
- }) |
|
| 342 |
- |
|
| 343 |
- t.Run("ParsePortRange", func(t *testing.T) {
|
|
| 344 |
- tests := []struct {
|
|
| 345 |
- in string |
|
| 346 |
- portRange PortRange // output of ParsePortRange() and Range() |
|
| 347 |
- str string // output of String(). If "", use in. |
|
| 348 |
- |
|
| 349 |
- }{
|
|
| 350 |
- // Zero port |
|
| 351 |
- {
|
|
| 352 |
- in: "0-1234/tcp", |
|
| 353 |
- portRange: portRangeFrom(0, 1234, TCP), |
|
| 354 |
- str: "0-1234/tcp", |
|
| 355 |
- }, |
|
| 356 |
- // Max valid port |
|
| 357 |
- {
|
|
| 358 |
- in: "1234-65535/tcp", |
|
| 359 |
- portRange: portRangeFrom(1234, 65535, TCP), |
|
| 360 |
- str: "1234-65535/tcp", |
|
| 361 |
- }, |
|
| 362 |
- // Simple valid ports |
|
| 363 |
- {
|
|
| 364 |
- in: "1234-4567/tcp", |
|
| 365 |
- portRange: portRangeFrom(1234, 4567, TCP), |
|
| 366 |
- str: "1234-4567/tcp", |
|
| 367 |
- }, |
|
| 368 |
- {
|
|
| 369 |
- in: "1234-4567/udp", |
|
| 370 |
- portRange: portRangeFrom(1234, 4567, UDP), |
|
| 371 |
- str: "1234-4567/udp", |
|
| 372 |
- }, |
|
| 373 |
- // Default protocol is tcp |
|
| 374 |
- {
|
|
| 375 |
- in: "1234-4567", |
|
| 376 |
- portRange: portRangeFrom(1234, 4567, TCP), |
|
| 377 |
- str: "1234-4567/tcp", |
|
| 378 |
- }, |
|
| 379 |
- // Default protocol is tcp |
|
| 380 |
- {
|
|
| 381 |
- in: "1234-4567/", |
|
| 382 |
- portRange: portRangeFrom(1234, 4567, TCP), |
|
| 383 |
- str: "1234-4567/tcp", |
|
| 384 |
- }, |
|
| 385 |
- {
|
|
| 386 |
- in: "1234/tcp", |
|
| 387 |
- portRange: portRangeFrom(1234, 1234, TCP), |
|
| 388 |
- str: "1234/tcp", |
|
| 389 |
- }, |
|
| 390 |
- {
|
|
| 391 |
- in: "1234", |
|
| 392 |
- portRange: portRangeFrom(1234, 1234, TCP), |
|
| 393 |
- str: "1234/tcp", |
|
| 394 |
- }, |
|
| 395 |
- {
|
|
| 396 |
- in: "1234-5678/tcp:ipv6only", |
|
| 397 |
- portRange: portRangeFrom(1234, 5678, "tcp:ipv6only"), |
|
| 398 |
- str: "1234-5678/tcp:ipv6only", |
|
| 399 |
- }, |
|
| 400 |
- } |
|
| 401 |
- |
|
| 402 |
- for _, tc := range tests {
|
|
| 403 |
- t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
|
|
| 404 |
- got, err := ParsePortRange(tc.in) |
|
| 405 |
- assert.NilError(t, err) |
|
| 406 |
- assert.Equal(t, got, tc.portRange) |
|
| 407 |
- assert.Check(t, !got.IsZero()) |
|
| 408 |
- assert.Check(t, got.IsValid()) |
|
| 409 |
- |
|
| 410 |
- MustParsePortRange(tc.in) // should not panic |
|
| 411 |
- |
|
| 412 |
- // Check that ParsePortRange is a pure function. |
|
| 413 |
- got2, err := ParsePortRange(tc.in) |
|
| 414 |
- assert.NilError(t, err) |
|
| 415 |
- assert.Equal(t, got2, got) |
|
| 416 |
- |
|
| 417 |
- // Check that ParsePortRange(port.String()) is the identity function. |
|
| 418 |
- got3, err := ParsePortRange(got.String()) |
|
| 419 |
- assert.NilError(t, err) |
|
| 420 |
- assert.Equal(t, got3, got) |
|
| 421 |
- |
|
| 422 |
- // Check String() output |
|
| 423 |
- s := got.String() |
|
| 424 |
- wants := tc.str |
|
| 425 |
- if wants == "" {
|
|
| 426 |
- wants = tc.in |
|
| 427 |
- } |
|
| 428 |
- assert.Equal(t, s, wants) |
|
| 429 |
- |
|
| 430 |
- js := `"` + tc.in + `"` |
|
| 431 |
- var jsgot PortRange |
|
| 432 |
- err = json.Unmarshal([]byte(js), &jsgot) |
|
| 433 |
- assert.NilError(t, err) |
|
| 434 |
- assert.Equal(t, jsgot, got) |
|
| 435 |
- |
|
| 436 |
- jsb, err := json.Marshal(jsgot) |
|
| 437 |
- assert.NilError(t, err) |
|
| 438 |
- jswant := `"` + wants + `"` |
|
| 439 |
- assert.Equal(t, string(jsb), jswant) |
|
| 440 |
- |
|
| 441 |
- // Check Range() output |
|
| 442 |
- r := got.Range() |
|
| 443 |
- assert.Equal(t, r, tc.portRange) |
|
| 444 |
- }) |
|
| 445 |
- |
|
| 446 |
- t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 447 |
- pr1 := MustParsePortRange("1234-5678/tcp")
|
|
| 448 |
- pr2 := MustParsePortRange("1234-5678/TCP")
|
|
| 449 |
- pr3 := MustParsePortRange("1234-5678/tCp")
|
|
| 450 |
- assert.Equal(t, pr1, pr2) |
|
| 451 |
- assert.Equal(t, pr2, pr3) |
|
| 452 |
- }) |
|
| 453 |
- |
|
| 454 |
- negativeTests := []string{
|
|
| 455 |
- // Empty string |
|
| 456 |
- "", |
|
| 457 |
- // Whitespace-only string |
|
| 458 |
- " ", |
|
| 459 |
- // No port number |
|
| 460 |
- "/", |
|
| 461 |
- // No port number (protocol only) |
|
| 462 |
- "/tcp", |
|
| 463 |
- // Negative start port |
|
| 464 |
- "-1-1234", |
|
| 465 |
- // Negative end port |
|
| 466 |
- "1234--1", |
|
| 467 |
- // Too large start port |
|
| 468 |
- "65536-65537", |
|
| 469 |
- // Too large end port |
|
| 470 |
- "1234-65536", |
|
| 471 |
- // Non-numeric start port |
|
| 472 |
- "foo-1234", |
|
| 473 |
- // Non-numeric end port |
|
| 474 |
- "1234-bar", |
|
| 475 |
- // Start port greater than end port |
|
| 476 |
- "1234-1000", |
|
| 477 |
- // Garbage port range |
|
| 478 |
- "asd1234-5678/tcp", |
|
| 479 |
- } |
|
| 480 |
- |
|
| 481 |
- for _, s := range negativeTests {
|
|
| 482 |
- t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
|
|
| 483 |
- got, err := ParsePortRange(s) |
|
| 484 |
- assert.Check(t, err != nil) |
|
| 485 |
- assert.Check(t, got.IsZero()) |
|
| 486 |
- assert.Check(t, !got.IsValid()) |
|
| 487 |
- |
|
| 488 |
- // Skip JSON unmarshalling test for empty string as that should succeed. |
|
| 489 |
- // See test "Zero Value" above. |
|
| 490 |
- if s == "" {
|
|
| 491 |
- return |
|
| 492 |
- } |
|
| 493 |
- |
|
| 494 |
- var jsgot PortRange |
|
| 495 |
- js := []byte(`"` + s + `"`) |
|
| 496 |
- err = json.Unmarshal(js, &jsgot) |
|
| 497 |
- assert.Check(t, err != nil) |
|
| 498 |
- assert.Equal(t, jsgot, PortRange{})
|
|
| 499 |
- }) |
|
| 500 |
- } |
|
| 501 |
- } |
|
| 502 |
- }) |
|
| 503 |
- |
|
| 504 |
- t.Run("PortRange All()", func(t *testing.T) {
|
|
| 505 |
- tests := []struct {
|
|
| 506 |
- in string |
|
| 507 |
- want []Port |
|
| 508 |
- }{
|
|
| 509 |
- {
|
|
| 510 |
- in: "1000-1000/tcp", |
|
| 511 |
- want: []Port{portFrom(1000, TCP)},
|
|
| 512 |
- }, |
|
| 513 |
- {
|
|
| 514 |
- in: "1000-1002/tcp", |
|
| 515 |
- want: []Port{portFrom(1000, TCP), portFrom(1001, TCP), portFrom(1002, TCP)},
|
|
| 516 |
- }, |
|
| 517 |
- {
|
|
| 518 |
- in: "0-0/tcp", |
|
| 519 |
- want: []Port{portFrom(0, TCP)},
|
|
| 520 |
- }, |
|
| 521 |
- {
|
|
| 522 |
- in: "65535-65535/tcp", |
|
| 523 |
- want: []Port{portFrom(65535, TCP)},
|
|
| 524 |
- }, |
|
| 525 |
- {
|
|
| 526 |
- in: "65530-65535/tcp", |
|
| 527 |
- want: []Port{portFrom(65530, TCP), portFrom(65531, TCP), portFrom(65532, TCP), portFrom(65533, TCP), portFrom(65534, TCP), portFrom(65535, TCP)},
|
|
| 528 |
- }, |
|
| 529 |
- } |
|
| 530 |
- |
|
| 531 |
- for _, tc := range tests {
|
|
| 532 |
- pr := MustParsePortRange(tc.in) |
|
| 533 |
- ports := slices.Collect(pr.All()) |
|
| 534 |
- if !reflect.DeepEqual(ports, tc.want) {
|
|
| 535 |
- t.Errorf("PortRange.All() = %#v, want %#v", ports, tc.want)
|
|
| 536 |
- } |
|
| 537 |
- } |
|
| 538 |
- |
|
| 539 |
- t.Run("All() stop early", func(t *testing.T) {
|
|
| 540 |
- want := []Port{portFrom(1000, TCP), portFrom(1001, TCP)}
|
|
| 541 |
- pr := MustParsePortRange("1000-2000/tcp")
|
|
| 542 |
- var ports []Port |
|
| 543 |
- for p := range pr.All() {
|
|
| 544 |
- ports = append(ports, p) |
|
| 545 |
- if len(ports) == 2 {
|
|
| 546 |
- break |
|
| 547 |
- } |
|
| 548 |
- } |
|
| 549 |
- if !reflect.DeepEqual(ports, want) {
|
|
| 550 |
- t.Errorf("PortRange.All() = %#v, want %#v", ports, want)
|
|
| 551 |
- } |
|
| 552 |
- }) |
|
| 553 |
- }) |
|
| 554 |
-} |
|
| 555 |
- |
|
| 556 |
-func BenchmarkPortRangeAll(b *testing.B) {
|
|
| 557 |
- b.Run("Single Port", func(b *testing.B) {
|
|
| 558 |
- pr := MustParsePortRange("1234/tcp")
|
|
| 559 |
- b.ResetTimer() |
|
| 560 |
- for i := 0; i < b.N; i++ {
|
|
| 561 |
- var sink int64 |
|
| 562 |
- for p := range pr.All() {
|
|
| 563 |
- sink += int64(p.Num()) // prevent compiler optimization |
|
| 564 |
- } |
|
| 565 |
- if sink < 0 {
|
|
| 566 |
- b.Fatal("unreachable")
|
|
| 567 |
- } |
|
| 568 |
- } |
|
| 569 |
- }) |
|
| 570 |
- |
|
| 571 |
- b.Run("Range", func(b *testing.B) {
|
|
| 572 |
- pr := MustParsePortRange("0-65535/tcp")
|
|
| 573 |
- b.ResetTimer() |
|
| 574 |
- for i := 0; i < b.N; i++ {
|
|
| 575 |
- var sink int64 |
|
| 576 |
- for p := range pr.All() {
|
|
| 577 |
- sink += int64(p.Num()) // prevent compiler optimization |
|
| 578 |
- } |
|
| 579 |
- if sink < 0 {
|
|
| 580 |
- b.Fatal("unreachable")
|
|
| 581 |
- } |
|
| 582 |
- } |
|
| 583 |
- }) |
|
| 584 |
-} |
|
| 585 |
- |
|
| 586 |
-func portFrom(num uint16, proto NetworkProtocol) Port {
|
|
| 587 |
- p, ok := PortFrom(num, proto) |
|
| 588 |
- if !ok {
|
|
| 589 |
- panic("invalid port")
|
|
| 590 |
- } |
|
| 591 |
- return p |
|
| 592 |
-} |
|
| 593 |
- |
|
| 594 |
-func portRangeFrom(start, end uint16, proto NetworkProtocol) PortRange {
|
|
| 595 |
- pr, ok := PortRangeFrom(start, end, proto) |
|
| 596 |
- if !ok {
|
|
| 597 |
- panic("invalid port range")
|
|
| 598 |
- } |
|
| 599 |
- return pr |
|
| 600 |
-} |
| 601 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,346 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "iter" |
|
| 6 |
+ "net/netip" |
|
| 7 |
+ "strconv" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "unique" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// IPProtocol represents a network protocol for a port. |
|
| 13 |
+type IPProtocol string |
|
| 14 |
+ |
|
| 15 |
+const ( |
|
| 16 |
+ TCP IPProtocol = "tcp" |
|
| 17 |
+ UDP IPProtocol = "udp" |
|
| 18 |
+ SCTP IPProtocol = "sctp" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+// Sentinel port proto value for zero Port and PortRange values. |
|
| 22 |
+var protoZero unique.Handle[IPProtocol] |
|
| 23 |
+ |
|
| 24 |
+// Port is a type representing a single port number and protocol in the format "<portnum>/[<proto>]". |
|
| 25 |
+// |
|
| 26 |
+// The zero port value, i.e. Port{}, is invalid; use [ParsePort] to create a valid Port value.
|
|
| 27 |
+type Port struct {
|
|
| 28 |
+ num uint16 |
|
| 29 |
+ proto unique.Handle[IPProtocol] |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+// ParsePort parses s as a [Port]. |
|
| 33 |
+// |
|
| 34 |
+// It normalizes the provided protocol such that "80/tcp", "80/TCP", and "80/tCp" are equivalent. |
|
| 35 |
+// If a port number is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 36 |
+func ParsePort(s string) (Port, error) {
|
|
| 37 |
+ if s == "" {
|
|
| 38 |
+ return Port{}, errors.New("invalid port: value is empty")
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ port, proto, _ := strings.Cut(s, "/") |
|
| 42 |
+ |
|
| 43 |
+ portNum, err := parsePortNumber(port) |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return Port{}, fmt.Errorf("invalid port '%s': %w", port, err)
|
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ normalizedPortProto := normalizePortProto(proto) |
|
| 49 |
+ return Port{num: portNum, proto: normalizedPortProto}, nil
|
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// MustParsePort calls [ParsePort](s) and panics on error. |
|
| 53 |
+// |
|
| 54 |
+// It is intended for use in tests with hard-coded strings. |
|
| 55 |
+func MustParsePort(s string) Port {
|
|
| 56 |
+ p, err := ParsePort(s) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ panic(err) |
|
| 59 |
+ } |
|
| 60 |
+ return p |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// PortFrom returns a [Port] with the given number and protocol. |
|
| 64 |
+// |
|
| 65 |
+// If no protocol is specified (i.e. proto == ""), then PortFrom returns Port{}, false.
|
|
| 66 |
+func PortFrom(num uint16, proto IPProtocol) (p Port, ok bool) {
|
|
| 67 |
+ if proto == "" {
|
|
| 68 |
+ return Port{}, false
|
|
| 69 |
+ } |
|
| 70 |
+ normalized := normalizePortProto(string(proto)) |
|
| 71 |
+ return Port{num: num, proto: normalized}, true
|
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+// Num returns p's port number. |
|
| 75 |
+func (p Port) Num() uint16 {
|
|
| 76 |
+ return p.num |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Proto returns p's network protocol. |
|
| 80 |
+func (p Port) Proto() IPProtocol {
|
|
| 81 |
+ return p.proto.Value() |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// IsZero reports whether p is the zero value. |
|
| 85 |
+func (p Port) IsZero() bool {
|
|
| 86 |
+ return p.proto == protoZero |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// IsValid reports whether p is an initialized valid port (not the zero value). |
|
| 90 |
+func (p Port) IsValid() bool {
|
|
| 91 |
+ return p.proto != protoZero |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// String returns a string representation of the port in the format "<portnum>/<proto>". |
|
| 95 |
+// If the port is the zero value, it returns "invalid port". |
|
| 96 |
+func (p Port) String() string {
|
|
| 97 |
+ switch p.proto {
|
|
| 98 |
+ case protoZero: |
|
| 99 |
+ return "invalid port" |
|
| 100 |
+ default: |
|
| 101 |
+ return string(p.AppendTo(nil)) |
|
| 102 |
+ } |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+// AppendText implements [encoding.TextAppender] interface. |
|
| 106 |
+// It is the same as [Port.AppendTo] but returns an error to satisfy the interface. |
|
| 107 |
+func (p Port) AppendText(b []byte) ([]byte, error) {
|
|
| 108 |
+ return p.AppendTo(b), nil |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+// AppendTo appends a text encoding of p to b and returns the extended buffer. |
|
| 112 |
+func (p Port) AppendTo(b []byte) []byte {
|
|
| 113 |
+ if p.IsZero() {
|
|
| 114 |
+ return b |
|
| 115 |
+ } |
|
| 116 |
+ return fmt.Appendf(b, "%d/%s", p.num, p.proto.Value()) |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 120 |
+func (p Port) MarshalText() ([]byte, error) {
|
|
| 121 |
+ return p.AppendText(nil) |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 125 |
+func (p *Port) UnmarshalText(text []byte) error {
|
|
| 126 |
+ if len(text) == 0 {
|
|
| 127 |
+ *p = Port{}
|
|
| 128 |
+ return nil |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ port, err := ParsePort(string(text)) |
|
| 132 |
+ if err != nil {
|
|
| 133 |
+ return err |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ *p = port |
|
| 137 |
+ return nil |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+// Range returns a [PortRange] representing the single port. |
|
| 141 |
+func (p Port) Range() PortRange {
|
|
| 142 |
+ return PortRange{start: p.num, end: p.num, proto: p.proto}
|
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+// PortSet is a collection of structs indexed by [Port]. |
|
| 146 |
+type PortSet = map[Port]struct{}
|
|
| 147 |
+ |
|
| 148 |
+// PortBinding represents a binding between a Host IP address and a Host Port. |
|
| 149 |
+type PortBinding struct {
|
|
| 150 |
+ // HostIP is the host IP Address |
|
| 151 |
+ HostIP netip.Addr `json:"HostIp"` |
|
| 152 |
+ // HostPort is the host port number |
|
| 153 |
+ HostPort string `json:"HostPort"` |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+// PortMap is a collection of [PortBinding] indexed by [Port]. |
|
| 157 |
+type PortMap = map[Port][]PortBinding |
|
| 158 |
+ |
|
| 159 |
+// PortRange represents a range of port numbers and a protocol in the format "8000-9000/tcp". |
|
| 160 |
+// |
|
| 161 |
+// The zero port range value, i.e. PortRange{}, is invalid; use [ParsePortRange] to create a valid PortRange value.
|
|
| 162 |
+type PortRange struct {
|
|
| 163 |
+ start uint16 |
|
| 164 |
+ end uint16 |
|
| 165 |
+ proto unique.Handle[IPProtocol] |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+// ParsePortRange parses s as a [PortRange]. |
|
| 169 |
+// |
|
| 170 |
+// It normalizes the provided protocol such that "80-90/tcp", "80-90/TCP", and "80-90/tCp" are equivalent. |
|
| 171 |
+// If a port number range is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 172 |
+func ParsePortRange(s string) (PortRange, error) {
|
|
| 173 |
+ if s == "" {
|
|
| 174 |
+ return PortRange{}, errors.New("invalid port range: value is empty")
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ portRange, proto, _ := strings.Cut(s, "/") |
|
| 178 |
+ |
|
| 179 |
+ start, end, ok := strings.Cut(portRange, "-") |
|
| 180 |
+ startVal, err := parsePortNumber(start) |
|
| 181 |
+ if err != nil {
|
|
| 182 |
+ return PortRange{}, fmt.Errorf("invalid start port '%s': %w", start, err)
|
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ portProto := normalizePortProto(proto) |
|
| 186 |
+ |
|
| 187 |
+ if !ok || start == end {
|
|
| 188 |
+ return PortRange{start: startVal, end: startVal, proto: portProto}, nil
|
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ endVal, err := parsePortNumber(end) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ return PortRange{}, fmt.Errorf("invalid end port '%s': %w", end, err)
|
|
| 194 |
+ } |
|
| 195 |
+ if endVal < startVal {
|
|
| 196 |
+ return PortRange{}, errors.New("invalid port range: " + s)
|
|
| 197 |
+ } |
|
| 198 |
+ return PortRange{start: startVal, end: endVal, proto: portProto}, nil
|
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+// MustParsePortRange calls [ParsePortRange](s) and panics on error. |
|
| 202 |
+// It is intended for use in tests with hard-coded strings. |
|
| 203 |
+func MustParsePortRange(s string) PortRange {
|
|
| 204 |
+ pr, err := ParsePortRange(s) |
|
| 205 |
+ if err != nil {
|
|
| 206 |
+ panic(err) |
|
| 207 |
+ } |
|
| 208 |
+ return pr |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+// PortRangeFrom returns a [PortRange] with the given start and end port numbers and protocol. |
|
| 212 |
+// |
|
| 213 |
+// If end < start or no protocol is specified (i.e. proto == ""), then PortRangeFrom returns PortRange{}, false.
|
|
| 214 |
+func PortRangeFrom(start, end uint16, proto IPProtocol) (pr PortRange, ok bool) {
|
|
| 215 |
+ if end < start || proto == "" {
|
|
| 216 |
+ return PortRange{}, false
|
|
| 217 |
+ } |
|
| 218 |
+ normalized := normalizePortProto(string(proto)) |
|
| 219 |
+ return PortRange{start: start, end: end, proto: normalized}, true
|
|
| 220 |
+} |
|
| 221 |
+ |
|
| 222 |
+// Start returns pr's start port number. |
|
| 223 |
+func (pr PortRange) Start() uint16 {
|
|
| 224 |
+ return pr.start |
|
| 225 |
+} |
|
| 226 |
+ |
|
| 227 |
+// End returns pr's end port number. |
|
| 228 |
+func (pr PortRange) End() uint16 {
|
|
| 229 |
+ return pr.end |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+// Proto returns pr's network protocol. |
|
| 233 |
+func (pr PortRange) Proto() IPProtocol {
|
|
| 234 |
+ return pr.proto.Value() |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// IsZero reports whether pr is the zero value. |
|
| 238 |
+func (pr PortRange) IsZero() bool {
|
|
| 239 |
+ return pr.proto == protoZero |
|
| 240 |
+} |
|
| 241 |
+ |
|
| 242 |
+// IsValid reports whether pr is an initialized valid port range (not the zero value). |
|
| 243 |
+func (pr PortRange) IsValid() bool {
|
|
| 244 |
+ return pr.proto != protoZero |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+// String returns a string representation of the port range in the format "<start>-<end>/<proto>" or "<portnum>/<proto>" if start == end. |
|
| 248 |
+// If the port range is the zero value, it returns "invalid port range". |
|
| 249 |
+func (pr PortRange) String() string {
|
|
| 250 |
+ switch pr.proto {
|
|
| 251 |
+ case protoZero: |
|
| 252 |
+ return "invalid port range" |
|
| 253 |
+ default: |
|
| 254 |
+ return string(pr.AppendTo(nil)) |
|
| 255 |
+ } |
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+// AppendText implements [encoding.TextAppender] interface. |
|
| 259 |
+// It is the same as [PortRange.AppendTo] but returns an error to satisfy the interface. |
|
| 260 |
+func (pr PortRange) AppendText(b []byte) ([]byte, error) {
|
|
| 261 |
+ return pr.AppendTo(b), nil |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// AppendTo appends a text encoding of pr to b and returns the extended buffer. |
|
| 265 |
+func (pr PortRange) AppendTo(b []byte) []byte {
|
|
| 266 |
+ if pr.IsZero() {
|
|
| 267 |
+ return b |
|
| 268 |
+ } |
|
| 269 |
+ if pr.start == pr.end {
|
|
| 270 |
+ return fmt.Appendf(b, "%d/%s", pr.start, pr.proto.Value()) |
|
| 271 |
+ } |
|
| 272 |
+ return fmt.Appendf(b, "%d-%d/%s", pr.start, pr.end, pr.proto.Value()) |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 276 |
+func (pr PortRange) MarshalText() ([]byte, error) {
|
|
| 277 |
+ return pr.AppendText(nil) |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 281 |
+func (pr *PortRange) UnmarshalText(text []byte) error {
|
|
| 282 |
+ if len(text) == 0 {
|
|
| 283 |
+ *pr = PortRange{}
|
|
| 284 |
+ return nil |
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ portRange, err := ParsePortRange(string(text)) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return err |
|
| 290 |
+ } |
|
| 291 |
+ *pr = portRange |
|
| 292 |
+ return nil |
|
| 293 |
+} |
|
| 294 |
+ |
|
| 295 |
+// Range returns pr. |
|
| 296 |
+func (pr PortRange) Range() PortRange {
|
|
| 297 |
+ return pr |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+// All returns an iterator over all the individual ports in the range. |
|
| 301 |
+// |
|
| 302 |
+// For example: |
|
| 303 |
+// |
|
| 304 |
+// for port := range pr.All() {
|
|
| 305 |
+// // ... |
|
| 306 |
+// } |
|
| 307 |
+func (pr PortRange) All() iter.Seq[Port] {
|
|
| 308 |
+ return func(yield func(Port) bool) {
|
|
| 309 |
+ for i := uint32(pr.Start()); i <= uint32(pr.End()); i++ {
|
|
| 310 |
+ if !yield(Port{num: uint16(i), proto: pr.proto}) {
|
|
| 311 |
+ return |
|
| 312 |
+ } |
|
| 313 |
+ } |
|
| 314 |
+ } |
|
| 315 |
+} |
|
| 316 |
+ |
|
| 317 |
+// parsePortNumber parses rawPort into an int, unwrapping strconv errors |
|
| 318 |
+// and returning a single "out of range" error for any value outside 0–65535. |
|
| 319 |
+func parsePortNumber(rawPort string) (uint16, error) {
|
|
| 320 |
+ if rawPort == "" {
|
|
| 321 |
+ return 0, errors.New("value is empty")
|
|
| 322 |
+ } |
|
| 323 |
+ port, err := strconv.ParseUint(rawPort, 10, 16) |
|
| 324 |
+ if err != nil {
|
|
| 325 |
+ var numErr *strconv.NumError |
|
| 326 |
+ if errors.As(err, &numErr) {
|
|
| 327 |
+ err = numErr.Err |
|
| 328 |
+ } |
|
| 329 |
+ return 0, err |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ return uint16(port), nil |
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+// normalizePortProto normalizes the protocol string such that "tcp", "TCP", and "tCp" are equivalent. |
|
| 336 |
+// If proto is not specified, it defaults to "tcp". |
|
| 337 |
+func normalizePortProto(proto string) unique.Handle[IPProtocol] {
|
|
| 338 |
+ if proto == "" {
|
|
| 339 |
+ return unique.Make(TCP) |
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ proto = strings.ToLower(proto) |
|
| 343 |
+ |
|
| 344 |
+ return unique.Make(IPProtocol(proto)) |
|
| 345 |
+} |
| 0 | 346 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,600 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "reflect" |
|
| 6 |
+ "slices" |
|
| 7 |
+ "strings" |
|
| 8 |
+ "testing" |
|
| 9 |
+ |
|
| 10 |
+ "gotest.tools/v3/assert" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type TestRanger interface {
|
|
| 14 |
+ Range() PortRange |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+var _ TestRanger = Port{}
|
|
| 18 |
+var _ TestRanger = PortRange{}
|
|
| 19 |
+ |
|
| 20 |
+func TestPort(t *testing.T) {
|
|
| 21 |
+ t.Run("Zero Value", func(t *testing.T) {
|
|
| 22 |
+ var p Port |
|
| 23 |
+ assert.Check(t, p.IsZero()) |
|
| 24 |
+ assert.Check(t, !p.IsValid()) |
|
| 25 |
+ assert.Equal(t, p.String(), "invalid port") |
|
| 26 |
+ |
|
| 27 |
+ t.Run("Marshal Unmarshal", func(t *testing.T) {
|
|
| 28 |
+ var p Port |
|
| 29 |
+ bytes, err := p.MarshalText() |
|
| 30 |
+ assert.NilError(t, err) |
|
| 31 |
+ assert.Check(t, len(bytes) == 0) |
|
| 32 |
+ |
|
| 33 |
+ err = p.UnmarshalText([]byte(""))
|
|
| 34 |
+ assert.NilError(t, err) |
|
| 35 |
+ assert.Equal(t, p, Port{})
|
|
| 36 |
+ }) |
|
| 37 |
+ |
|
| 38 |
+ t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
|
|
| 39 |
+ var p Port |
|
| 40 |
+ bytes, err := json.Marshal(p) |
|
| 41 |
+ assert.NilError(t, err) |
|
| 42 |
+ assert.Equal(t, string(bytes), `""`) |
|
| 43 |
+ |
|
| 44 |
+ err = json.Unmarshal([]byte(`""`), &p) |
|
| 45 |
+ assert.NilError(t, err) |
|
| 46 |
+ assert.Equal(t, p, Port{})
|
|
| 47 |
+ }) |
|
| 48 |
+ }) |
|
| 49 |
+ |
|
| 50 |
+ t.Run("PortFrom", func(t *testing.T) {
|
|
| 51 |
+ tests := []struct {
|
|
| 52 |
+ num uint16 |
|
| 53 |
+ proto IPProtocol |
|
| 54 |
+ }{
|
|
| 55 |
+ {0, TCP},
|
|
| 56 |
+ {80, TCP},
|
|
| 57 |
+ {8080, TCP},
|
|
| 58 |
+ {65535, TCP},
|
|
| 59 |
+ {80, UDP},
|
|
| 60 |
+ {8080, SCTP},
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ for _, tc := range tests {
|
|
| 64 |
+ t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
|
|
| 65 |
+ p, ok := PortFrom(tc.num, tc.proto) |
|
| 66 |
+ assert.Check(t, ok) |
|
| 67 |
+ assert.Equal(t, p.Num(), tc.num) |
|
| 68 |
+ assert.Equal(t, p.Proto(), tc.proto) |
|
| 69 |
+ }) |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 73 |
+ pr1 := portFrom(1234, "tcp") |
|
| 74 |
+ pr2 := portFrom(1234, "TCP") |
|
| 75 |
+ pr3 := portFrom(1234, "tCp") |
|
| 76 |
+ assert.Equal(t, pr1, pr2) |
|
| 77 |
+ assert.Equal(t, pr2, pr3) |
|
| 78 |
+ }) |
|
| 79 |
+ |
|
| 80 |
+ negativeTests := []struct {
|
|
| 81 |
+ num uint16 |
|
| 82 |
+ proto IPProtocol |
|
| 83 |
+ }{
|
|
| 84 |
+ {0, ""},
|
|
| 85 |
+ {80, ""},
|
|
| 86 |
+ } |
|
| 87 |
+ for _, tc := range negativeTests {
|
|
| 88 |
+ t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
|
|
| 89 |
+ p, ok := PortFrom(tc.num, tc.proto) |
|
| 90 |
+ assert.Check(t, !ok) |
|
| 91 |
+ assert.Check(t, p.IsZero()) |
|
| 92 |
+ assert.Check(t, !p.IsValid()) |
|
| 93 |
+ assert.Equal(t, p.String(), "invalid port") |
|
| 94 |
+ }) |
|
| 95 |
+ } |
|
| 96 |
+ }) |
|
| 97 |
+ |
|
| 98 |
+ t.Run("ParsePort", func(t *testing.T) {
|
|
| 99 |
+ tests := []struct {
|
|
| 100 |
+ in string |
|
| 101 |
+ port Port // output of ParsePort() |
|
| 102 |
+ str string // output of String(). |
|
| 103 |
+ portRange PortRange // output of Range() |
|
| 104 |
+ }{
|
|
| 105 |
+ // Zero port |
|
| 106 |
+ {
|
|
| 107 |
+ in: "0/tcp", |
|
| 108 |
+ port: portFrom(0, TCP), |
|
| 109 |
+ str: "0/tcp", |
|
| 110 |
+ portRange: portRangeFrom(0, 0, TCP), |
|
| 111 |
+ }, |
|
| 112 |
+ // Max valid port |
|
| 113 |
+ {
|
|
| 114 |
+ in: "65535/tcp", |
|
| 115 |
+ port: portFrom(65535, TCP), |
|
| 116 |
+ str: "65535/tcp", |
|
| 117 |
+ portRange: portRangeFrom(65535, 65535, TCP), |
|
| 118 |
+ }, |
|
| 119 |
+ // Simple valid ports |
|
| 120 |
+ {
|
|
| 121 |
+ in: "1234/tcp", |
|
| 122 |
+ port: portFrom(1234, TCP), |
|
| 123 |
+ str: "1234/tcp", |
|
| 124 |
+ portRange: portRangeFrom(1234, 1234, TCP), |
|
| 125 |
+ }, |
|
| 126 |
+ {
|
|
| 127 |
+ in: "1234/udp", |
|
| 128 |
+ port: portFrom(1234, UDP), |
|
| 129 |
+ str: "1234/udp", |
|
| 130 |
+ portRange: portRangeFrom(1234, 1234, UDP), |
|
| 131 |
+ }, |
|
| 132 |
+ {
|
|
| 133 |
+ in: "1234/sctp", |
|
| 134 |
+ port: portFrom(1234, SCTP), |
|
| 135 |
+ str: "1234/sctp", |
|
| 136 |
+ portRange: portRangeFrom(1234, 1234, SCTP), |
|
| 137 |
+ }, |
|
| 138 |
+ // Default protocol is tcp |
|
| 139 |
+ {
|
|
| 140 |
+ in: "1234", |
|
| 141 |
+ port: portFrom(1234, TCP), |
|
| 142 |
+ str: "1234/tcp", |
|
| 143 |
+ portRange: portRangeFrom(1234, 1234, TCP), |
|
| 144 |
+ }, |
|
| 145 |
+ // Default protocol is tcp |
|
| 146 |
+ {
|
|
| 147 |
+ in: "1234/", |
|
| 148 |
+ port: portFrom(1234, TCP), |
|
| 149 |
+ str: "1234/tcp", |
|
| 150 |
+ portRange: portRangeFrom(1234, 1234, TCP), |
|
| 151 |
+ }, |
|
| 152 |
+ {
|
|
| 153 |
+ in: "1234/tcp:ipv6only", |
|
| 154 |
+ port: portFrom(1234, "tcp:ipv6only"), |
|
| 155 |
+ str: "1234/tcp:ipv6only", |
|
| 156 |
+ portRange: portRangeFrom(1234, 1234, "tcp:ipv6only"), |
|
| 157 |
+ }, |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ for _, tc := range tests {
|
|
| 161 |
+ t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
|
|
| 162 |
+ got, err := ParsePort(tc.in) |
|
| 163 |
+ assert.NilError(t, err) |
|
| 164 |
+ assert.Equal(t, got, tc.port) |
|
| 165 |
+ |
|
| 166 |
+ MustParsePort(tc.in) // should not panic |
|
| 167 |
+ |
|
| 168 |
+ assert.Check(t, !got.IsZero()) |
|
| 169 |
+ assert.Check(t, got.IsValid()) |
|
| 170 |
+ |
|
| 171 |
+ // Check that ParsePort is a pure function. |
|
| 172 |
+ got2, err := ParsePort(tc.in) |
|
| 173 |
+ assert.NilError(t, err) |
|
| 174 |
+ assert.Equal(t, got2, got) |
|
| 175 |
+ |
|
| 176 |
+ // Check that ParsePort(port.String()) is the identity function. |
|
| 177 |
+ got3, err := ParsePort(got.String()) |
|
| 178 |
+ assert.NilError(t, err) |
|
| 179 |
+ assert.Equal(t, got3, got) |
|
| 180 |
+ |
|
| 181 |
+ // Check String() output |
|
| 182 |
+ s := got.String() |
|
| 183 |
+ wants := tc.str |
|
| 184 |
+ if wants == "" {
|
|
| 185 |
+ wants = tc.in |
|
| 186 |
+ } |
|
| 187 |
+ assert.Equal(t, s, wants) |
|
| 188 |
+ |
|
| 189 |
+ js := `"` + tc.in + `"` |
|
| 190 |
+ var jsgot Port |
|
| 191 |
+ err = json.Unmarshal([]byte(js), &jsgot) |
|
| 192 |
+ assert.NilError(t, err) |
|
| 193 |
+ assert.Equal(t, jsgot, got) |
|
| 194 |
+ |
|
| 195 |
+ jsb, err := json.Marshal(jsgot) |
|
| 196 |
+ assert.NilError(t, err) |
|
| 197 |
+ |
|
| 198 |
+ jswant := `"` + wants + `"` |
|
| 199 |
+ assert.Equal(t, string(jsb), jswant) |
|
| 200 |
+ |
|
| 201 |
+ // Check Range() output |
|
| 202 |
+ r := got.Range() |
|
| 203 |
+ assert.Equal(t, r, tc.portRange) |
|
| 204 |
+ }) |
|
| 205 |
+ } |
|
| 206 |
+ |
|
| 207 |
+ t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 208 |
+ p1 := MustParsePort("1234/tcp")
|
|
| 209 |
+ p2 := MustParsePort("1234/TCP")
|
|
| 210 |
+ p3 := MustParsePort("1234/tCp")
|
|
| 211 |
+ assert.Equal(t, p1, p2) |
|
| 212 |
+ assert.Equal(t, p2, p3) |
|
| 213 |
+ }) |
|
| 214 |
+ |
|
| 215 |
+ negativeTests := []string{
|
|
| 216 |
+ // Empty string |
|
| 217 |
+ "", |
|
| 218 |
+ // Whitespace-only string |
|
| 219 |
+ " ", |
|
| 220 |
+ // No port number |
|
| 221 |
+ "/", |
|
| 222 |
+ // No port number (protocol only) |
|
| 223 |
+ "/tcp", |
|
| 224 |
+ // Negative port |
|
| 225 |
+ "-1", |
|
| 226 |
+ // Too large port |
|
| 227 |
+ "65536", |
|
| 228 |
+ // Non-numeric port |
|
| 229 |
+ "foo", |
|
| 230 |
+ // Port range instead of single port |
|
| 231 |
+ "1234-1240/udp", |
|
| 232 |
+ // Port range instead of single port without protocol |
|
| 233 |
+ "1234-1240", |
|
| 234 |
+ // Garbage port |
|
| 235 |
+ "asd1234/tcp", |
|
| 236 |
+ } |
|
| 237 |
+ |
|
| 238 |
+ for _, s := range negativeTests {
|
|
| 239 |
+ t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
|
|
| 240 |
+ got, err := ParsePort(s) |
|
| 241 |
+ assert.ErrorContains(t, err, "invalid port") |
|
| 242 |
+ assert.Check(t, got.IsZero()) |
|
| 243 |
+ assert.Check(t, !got.IsValid()) |
|
| 244 |
+ |
|
| 245 |
+ // Skip JSON unmarshalling test for empty string as that should succeed. |
|
| 246 |
+ // See test "Zero Value" above. |
|
| 247 |
+ if s == "" {
|
|
| 248 |
+ return |
|
| 249 |
+ } |
|
| 250 |
+ |
|
| 251 |
+ var jsgot Port |
|
| 252 |
+ js := []byte(`"` + s + `"`) |
|
| 253 |
+ err = json.Unmarshal(js, &jsgot) |
|
| 254 |
+ assert.ErrorContains(t, err, "invalid port") |
|
| 255 |
+ assert.Equal(t, jsgot, Port{})
|
|
| 256 |
+ }) |
|
| 257 |
+ } |
|
| 258 |
+ }) |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func TestPortRange(t *testing.T) {
|
|
| 262 |
+ t.Run("Zero Value", func(t *testing.T) {
|
|
| 263 |
+ var pr PortRange |
|
| 264 |
+ assert.Check(t, pr.IsZero()) |
|
| 265 |
+ assert.Check(t, !pr.IsValid()) |
|
| 266 |
+ assert.Equal(t, pr.String(), "invalid port range") |
|
| 267 |
+ |
|
| 268 |
+ t.Run("Marshal Unmarshal", func(t *testing.T) {
|
|
| 269 |
+ var pr PortRange |
|
| 270 |
+ bytes, err := pr.MarshalText() |
|
| 271 |
+ assert.NilError(t, err) |
|
| 272 |
+ assert.Check(t, len(bytes) == 0) |
|
| 273 |
+ |
|
| 274 |
+ err = pr.UnmarshalText([]byte(""))
|
|
| 275 |
+ assert.NilError(t, err) |
|
| 276 |
+ assert.Equal(t, pr, PortRange{})
|
|
| 277 |
+ }) |
|
| 278 |
+ |
|
| 279 |
+ t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
|
|
| 280 |
+ var pr PortRange |
|
| 281 |
+ bytes, err := json.Marshal(pr) |
|
| 282 |
+ assert.NilError(t, err) |
|
| 283 |
+ assert.Equal(t, string(bytes), `""`) |
|
| 284 |
+ |
|
| 285 |
+ err = json.Unmarshal([]byte(`""`), &pr) |
|
| 286 |
+ assert.NilError(t, err) |
|
| 287 |
+ assert.Equal(t, pr, PortRange{})
|
|
| 288 |
+ }) |
|
| 289 |
+ }) |
|
| 290 |
+ |
|
| 291 |
+ t.Run("PortRangeFrom", func(t *testing.T) {
|
|
| 292 |
+ tests := []struct {
|
|
| 293 |
+ start uint16 |
|
| 294 |
+ end uint16 |
|
| 295 |
+ proto IPProtocol |
|
| 296 |
+ }{
|
|
| 297 |
+ {0, 0, TCP},
|
|
| 298 |
+ {0, 1234, TCP},
|
|
| 299 |
+ {80, 80, TCP},
|
|
| 300 |
+ {80, 8080, TCP},
|
|
| 301 |
+ {1234, 65535, TCP},
|
|
| 302 |
+ {80, 80, UDP},
|
|
| 303 |
+ {80, 8080, SCTP},
|
|
| 304 |
+ } |
|
| 305 |
+ |
|
| 306 |
+ for _, tc := range tests {
|
|
| 307 |
+ t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
|
|
| 308 |
+ pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto) |
|
| 309 |
+ assert.Check(t, ok) |
|
| 310 |
+ assert.Equal(t, pr.Start(), tc.start) |
|
| 311 |
+ assert.Equal(t, pr.End(), tc.end) |
|
| 312 |
+ assert.Equal(t, pr.Proto(), tc.proto) |
|
| 313 |
+ }) |
|
| 314 |
+ } |
|
| 315 |
+ |
|
| 316 |
+ t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 317 |
+ pr1, _ := PortRangeFrom(1234, 5678, "tcp") |
|
| 318 |
+ pr2, _ := PortRangeFrom(1234, 5678, "TCP") |
|
| 319 |
+ pr3, _ := PortRangeFrom(1234, 5678, "tCp") |
|
| 320 |
+ assert.Equal(t, pr1, pr2) |
|
| 321 |
+ assert.Equal(t, pr2, pr3) |
|
| 322 |
+ }) |
|
| 323 |
+ |
|
| 324 |
+ negativeTests := []struct {
|
|
| 325 |
+ start uint16 |
|
| 326 |
+ end uint16 |
|
| 327 |
+ proto IPProtocol |
|
| 328 |
+ }{
|
|
| 329 |
+ {1234, 80, TCP}, // end < start
|
|
| 330 |
+ {0, 0, ""}, // empty protocol
|
|
| 331 |
+ } |
|
| 332 |
+ for _, tc := range negativeTests {
|
|
| 333 |
+ t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
|
|
| 334 |
+ pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto) |
|
| 335 |
+ assert.Check(t, !ok) |
|
| 336 |
+ assert.Check(t, pr.IsZero()) |
|
| 337 |
+ assert.Check(t, !pr.IsValid()) |
|
| 338 |
+ }) |
|
| 339 |
+ } |
|
| 340 |
+ }) |
|
| 341 |
+ |
|
| 342 |
+ t.Run("ParsePortRange", func(t *testing.T) {
|
|
| 343 |
+ tests := []struct {
|
|
| 344 |
+ in string |
|
| 345 |
+ portRange PortRange // output of ParsePortRange() and Range() |
|
| 346 |
+ str string // output of String(). If "", use in. |
|
| 347 |
+ |
|
| 348 |
+ }{
|
|
| 349 |
+ // Zero port |
|
| 350 |
+ {
|
|
| 351 |
+ in: "0-1234/tcp", |
|
| 352 |
+ portRange: portRangeFrom(0, 1234, TCP), |
|
| 353 |
+ str: "0-1234/tcp", |
|
| 354 |
+ }, |
|
| 355 |
+ // Max valid port |
|
| 356 |
+ {
|
|
| 357 |
+ in: "1234-65535/tcp", |
|
| 358 |
+ portRange: portRangeFrom(1234, 65535, TCP), |
|
| 359 |
+ str: "1234-65535/tcp", |
|
| 360 |
+ }, |
|
| 361 |
+ // Simple valid ports |
|
| 362 |
+ {
|
|
| 363 |
+ in: "1234-4567/tcp", |
|
| 364 |
+ portRange: portRangeFrom(1234, 4567, TCP), |
|
| 365 |
+ str: "1234-4567/tcp", |
|
| 366 |
+ }, |
|
| 367 |
+ {
|
|
| 368 |
+ in: "1234-4567/udp", |
|
| 369 |
+ portRange: portRangeFrom(1234, 4567, UDP), |
|
| 370 |
+ str: "1234-4567/udp", |
|
| 371 |
+ }, |
|
| 372 |
+ // Default protocol is tcp |
|
| 373 |
+ {
|
|
| 374 |
+ in: "1234-4567", |
|
| 375 |
+ portRange: portRangeFrom(1234, 4567, TCP), |
|
| 376 |
+ str: "1234-4567/tcp", |
|
| 377 |
+ }, |
|
| 378 |
+ // Default protocol is tcp |
|
| 379 |
+ {
|
|
| 380 |
+ in: "1234-4567/", |
|
| 381 |
+ portRange: portRangeFrom(1234, 4567, TCP), |
|
| 382 |
+ str: "1234-4567/tcp", |
|
| 383 |
+ }, |
|
| 384 |
+ {
|
|
| 385 |
+ in: "1234/tcp", |
|
| 386 |
+ portRange: portRangeFrom(1234, 1234, TCP), |
|
| 387 |
+ str: "1234/tcp", |
|
| 388 |
+ }, |
|
| 389 |
+ {
|
|
| 390 |
+ in: "1234", |
|
| 391 |
+ portRange: portRangeFrom(1234, 1234, TCP), |
|
| 392 |
+ str: "1234/tcp", |
|
| 393 |
+ }, |
|
| 394 |
+ {
|
|
| 395 |
+ in: "1234-5678/tcp:ipv6only", |
|
| 396 |
+ portRange: portRangeFrom(1234, 5678, "tcp:ipv6only"), |
|
| 397 |
+ str: "1234-5678/tcp:ipv6only", |
|
| 398 |
+ }, |
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ for _, tc := range tests {
|
|
| 402 |
+ t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
|
|
| 403 |
+ got, err := ParsePortRange(tc.in) |
|
| 404 |
+ assert.NilError(t, err) |
|
| 405 |
+ assert.Equal(t, got, tc.portRange) |
|
| 406 |
+ assert.Check(t, !got.IsZero()) |
|
| 407 |
+ assert.Check(t, got.IsValid()) |
|
| 408 |
+ |
|
| 409 |
+ MustParsePortRange(tc.in) // should not panic |
|
| 410 |
+ |
|
| 411 |
+ // Check that ParsePortRange is a pure function. |
|
| 412 |
+ got2, err := ParsePortRange(tc.in) |
|
| 413 |
+ assert.NilError(t, err) |
|
| 414 |
+ assert.Equal(t, got2, got) |
|
| 415 |
+ |
|
| 416 |
+ // Check that ParsePortRange(port.String()) is the identity function. |
|
| 417 |
+ got3, err := ParsePortRange(got.String()) |
|
| 418 |
+ assert.NilError(t, err) |
|
| 419 |
+ assert.Equal(t, got3, got) |
|
| 420 |
+ |
|
| 421 |
+ // Check String() output |
|
| 422 |
+ s := got.String() |
|
| 423 |
+ wants := tc.str |
|
| 424 |
+ if wants == "" {
|
|
| 425 |
+ wants = tc.in |
|
| 426 |
+ } |
|
| 427 |
+ assert.Equal(t, s, wants) |
|
| 428 |
+ |
|
| 429 |
+ js := `"` + tc.in + `"` |
|
| 430 |
+ var jsgot PortRange |
|
| 431 |
+ err = json.Unmarshal([]byte(js), &jsgot) |
|
| 432 |
+ assert.NilError(t, err) |
|
| 433 |
+ assert.Equal(t, jsgot, got) |
|
| 434 |
+ |
|
| 435 |
+ jsb, err := json.Marshal(jsgot) |
|
| 436 |
+ assert.NilError(t, err) |
|
| 437 |
+ jswant := `"` + wants + `"` |
|
| 438 |
+ assert.Equal(t, string(jsb), jswant) |
|
| 439 |
+ |
|
| 440 |
+ // Check Range() output |
|
| 441 |
+ r := got.Range() |
|
| 442 |
+ assert.Equal(t, r, tc.portRange) |
|
| 443 |
+ }) |
|
| 444 |
+ |
|
| 445 |
+ t.Run("Normalize Protocol", func(t *testing.T) {
|
|
| 446 |
+ pr1 := MustParsePortRange("1234-5678/tcp")
|
|
| 447 |
+ pr2 := MustParsePortRange("1234-5678/TCP")
|
|
| 448 |
+ pr3 := MustParsePortRange("1234-5678/tCp")
|
|
| 449 |
+ assert.Equal(t, pr1, pr2) |
|
| 450 |
+ assert.Equal(t, pr2, pr3) |
|
| 451 |
+ }) |
|
| 452 |
+ |
|
| 453 |
+ negativeTests := []string{
|
|
| 454 |
+ // Empty string |
|
| 455 |
+ "", |
|
| 456 |
+ // Whitespace-only string |
|
| 457 |
+ " ", |
|
| 458 |
+ // No port number |
|
| 459 |
+ "/", |
|
| 460 |
+ // No port number (protocol only) |
|
| 461 |
+ "/tcp", |
|
| 462 |
+ // Negative start port |
|
| 463 |
+ "-1-1234", |
|
| 464 |
+ // Negative end port |
|
| 465 |
+ "1234--1", |
|
| 466 |
+ // Too large start port |
|
| 467 |
+ "65536-65537", |
|
| 468 |
+ // Too large end port |
|
| 469 |
+ "1234-65536", |
|
| 470 |
+ // Non-numeric start port |
|
| 471 |
+ "foo-1234", |
|
| 472 |
+ // Non-numeric end port |
|
| 473 |
+ "1234-bar", |
|
| 474 |
+ // Start port greater than end port |
|
| 475 |
+ "1234-1000", |
|
| 476 |
+ // Garbage port range |
|
| 477 |
+ "asd1234-5678/tcp", |
|
| 478 |
+ } |
|
| 479 |
+ |
|
| 480 |
+ for _, s := range negativeTests {
|
|
| 481 |
+ t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
|
|
| 482 |
+ got, err := ParsePortRange(s) |
|
| 483 |
+ assert.Check(t, err != nil) |
|
| 484 |
+ assert.Check(t, got.IsZero()) |
|
| 485 |
+ assert.Check(t, !got.IsValid()) |
|
| 486 |
+ |
|
| 487 |
+ // Skip JSON unmarshalling test for empty string as that should succeed. |
|
| 488 |
+ // See test "Zero Value" above. |
|
| 489 |
+ if s == "" {
|
|
| 490 |
+ return |
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ var jsgot PortRange |
|
| 494 |
+ js := []byte(`"` + s + `"`) |
|
| 495 |
+ err = json.Unmarshal(js, &jsgot) |
|
| 496 |
+ assert.Check(t, err != nil) |
|
| 497 |
+ assert.Equal(t, jsgot, PortRange{})
|
|
| 498 |
+ }) |
|
| 499 |
+ } |
|
| 500 |
+ } |
|
| 501 |
+ }) |
|
| 502 |
+ |
|
| 503 |
+ t.Run("PortRange All()", func(t *testing.T) {
|
|
| 504 |
+ tests := []struct {
|
|
| 505 |
+ in string |
|
| 506 |
+ want []Port |
|
| 507 |
+ }{
|
|
| 508 |
+ {
|
|
| 509 |
+ in: "1000-1000/tcp", |
|
| 510 |
+ want: []Port{portFrom(1000, TCP)},
|
|
| 511 |
+ }, |
|
| 512 |
+ {
|
|
| 513 |
+ in: "1000-1002/tcp", |
|
| 514 |
+ want: []Port{portFrom(1000, TCP), portFrom(1001, TCP), portFrom(1002, TCP)},
|
|
| 515 |
+ }, |
|
| 516 |
+ {
|
|
| 517 |
+ in: "0-0/tcp", |
|
| 518 |
+ want: []Port{portFrom(0, TCP)},
|
|
| 519 |
+ }, |
|
| 520 |
+ {
|
|
| 521 |
+ in: "65535-65535/tcp", |
|
| 522 |
+ want: []Port{portFrom(65535, TCP)},
|
|
| 523 |
+ }, |
|
| 524 |
+ {
|
|
| 525 |
+ in: "65530-65535/tcp", |
|
| 526 |
+ want: []Port{portFrom(65530, TCP), portFrom(65531, TCP), portFrom(65532, TCP), portFrom(65533, TCP), portFrom(65534, TCP), portFrom(65535, TCP)},
|
|
| 527 |
+ }, |
|
| 528 |
+ } |
|
| 529 |
+ |
|
| 530 |
+ for _, tc := range tests {
|
|
| 531 |
+ pr := MustParsePortRange(tc.in) |
|
| 532 |
+ ports := slices.Collect(pr.All()) |
|
| 533 |
+ if !reflect.DeepEqual(ports, tc.want) {
|
|
| 534 |
+ t.Errorf("PortRange.All() = %#v, want %#v", ports, tc.want)
|
|
| 535 |
+ } |
|
| 536 |
+ } |
|
| 537 |
+ |
|
| 538 |
+ t.Run("All() stop early", func(t *testing.T) {
|
|
| 539 |
+ want := []Port{portFrom(1000, TCP), portFrom(1001, TCP)}
|
|
| 540 |
+ pr := MustParsePortRange("1000-2000/tcp")
|
|
| 541 |
+ var ports []Port |
|
| 542 |
+ for p := range pr.All() {
|
|
| 543 |
+ ports = append(ports, p) |
|
| 544 |
+ if len(ports) == 2 {
|
|
| 545 |
+ break |
|
| 546 |
+ } |
|
| 547 |
+ } |
|
| 548 |
+ if !reflect.DeepEqual(ports, want) {
|
|
| 549 |
+ t.Errorf("PortRange.All() = %#v, want %#v", ports, want)
|
|
| 550 |
+ } |
|
| 551 |
+ }) |
|
| 552 |
+ }) |
|
| 553 |
+} |
|
| 554 |
+ |
|
| 555 |
+func BenchmarkPortRangeAll(b *testing.B) {
|
|
| 556 |
+ b.Run("Single Port", func(b *testing.B) {
|
|
| 557 |
+ pr := MustParsePortRange("1234/tcp")
|
|
| 558 |
+ b.ResetTimer() |
|
| 559 |
+ for i := 0; i < b.N; i++ {
|
|
| 560 |
+ var sink int64 |
|
| 561 |
+ for p := range pr.All() {
|
|
| 562 |
+ sink += int64(p.Num()) // prevent compiler optimization |
|
| 563 |
+ } |
|
| 564 |
+ if sink < 0 {
|
|
| 565 |
+ b.Fatal("unreachable")
|
|
| 566 |
+ } |
|
| 567 |
+ } |
|
| 568 |
+ }) |
|
| 569 |
+ |
|
| 570 |
+ b.Run("Range", func(b *testing.B) {
|
|
| 571 |
+ pr := MustParsePortRange("0-65535/tcp")
|
|
| 572 |
+ b.ResetTimer() |
|
| 573 |
+ for i := 0; i < b.N; i++ {
|
|
| 574 |
+ var sink int64 |
|
| 575 |
+ for p := range pr.All() {
|
|
| 576 |
+ sink += int64(p.Num()) // prevent compiler optimization |
|
| 577 |
+ } |
|
| 578 |
+ if sink < 0 {
|
|
| 579 |
+ b.Fatal("unreachable")
|
|
| 580 |
+ } |
|
| 581 |
+ } |
|
| 582 |
+ }) |
|
| 583 |
+} |
|
| 584 |
+ |
|
| 585 |
+func portFrom(num uint16, proto IPProtocol) Port {
|
|
| 586 |
+ p, ok := PortFrom(num, proto) |
|
| 587 |
+ if !ok {
|
|
| 588 |
+ panic("invalid port")
|
|
| 589 |
+ } |
|
| 590 |
+ return p |
|
| 591 |
+} |
|
| 592 |
+ |
|
| 593 |
+func portRangeFrom(start, end uint16, proto IPProtocol) PortRange {
|
|
| 594 |
+ pr, ok := PortRangeFrom(start, end, proto) |
|
| 595 |
+ if !ok {
|
|
| 596 |
+ panic("invalid port range")
|
|
| 597 |
+ } |
|
| 598 |
+ return pr |
|
| 599 |
+} |
| ... | ... |
@@ -21,8 +21,8 @@ import ( |
| 21 | 21 |
"github.com/moby/buildkit/frontend/dockerfile/instructions" |
| 22 | 22 |
"github.com/moby/buildkit/frontend/dockerfile/parser" |
| 23 | 23 |
"github.com/moby/buildkit/frontend/dockerfile/shell" |
| 24 |
- "github.com/moby/moby/api/types/container" |
|
| 25 | 24 |
"github.com/moby/moby/api/types/jsonstream" |
| 25 |
+ "github.com/moby/moby/api/types/network" |
|
| 26 | 26 |
"github.com/moby/moby/v2/daemon/builder" |
| 27 | 27 |
"github.com/moby/moby/v2/daemon/internal/image" |
| 28 | 28 |
"github.com/moby/moby/v2/daemon/internal/netiputil" |
| ... | ... |
@@ -533,7 +533,7 @@ func dispatchExpose(ctx context.Context, d dispatchRequest, c *instructions.Expo |
| 533 | 533 |
} |
| 534 | 534 |
|
| 535 | 535 |
if d.state.runConfig.ExposedPorts == nil {
|
| 536 |
- d.state.runConfig.ExposedPorts = make(container.PortSet) |
|
| 536 |
+ d.state.runConfig.ExposedPorts = make(network.PortSet) |
|
| 537 | 537 |
} |
| 538 | 538 |
for p := range ps {
|
| 539 | 539 |
d.state.runConfig.ExposedPorts[p] = struct{}{}
|
| ... | ... |
@@ -546,10 +546,10 @@ func dispatchExpose(ctx context.Context, d dispatchRequest, c *instructions.Expo |
| 546 | 546 |
// |
| 547 | 547 |
// parsePortSpecs receives port specs in the format of ip:public:private/proto and parses |
| 548 | 548 |
// these in to the internal types |
| 549 |
-func parsePortSpecs(ports []string) (map[container.Port]struct{}, map[container.Port][]container.PortBinding, error) {
|
|
| 549 |
+func parsePortSpecs(ports []string) (map[network.Port]struct{}, network.PortMap, error) {
|
|
| 550 | 550 |
var ( |
| 551 |
- exposedPorts = make(map[container.Port]struct{}, len(ports))
|
|
| 552 |
- bindings = make(map[container.Port][]container.PortBinding) |
|
| 551 |
+ exposedPorts = make(map[network.Port]struct{}, len(ports))
|
|
| 552 |
+ bindings = make(network.PortMap) |
|
| 553 | 553 |
) |
| 554 | 554 |
for _, p := range ports {
|
| 555 | 555 |
portMappings, err := parsePortSpec(p) |
| ... | ... |
@@ -571,8 +571,8 @@ func parsePortSpecs(ports []string) (map[container.Port]struct{}, map[container.
|
| 571 | 571 |
|
| 572 | 572 |
// Copied and modified from https://github.com/docker/go-connections/blob/c296721c0d56d3acad2973376ded214103a4fd2e/nat/nat.go#L172-L237 |
| 573 | 573 |
// |
| 574 |
-// parsePortSpec parses a port specification string into a slice of [container.PortMap] |
|
| 575 |
-func parsePortSpec(rawPort string) ([]container.PortMap, error) {
|
|
| 574 |
+// parsePortSpec parses a port specification string into a slice of [network.PortMap] |
|
| 575 |
+func parsePortSpec(rawPort string) ([]network.PortMap, error) {
|
|
| 576 | 576 |
ip, hostPort, containerPort := splitParts(rawPort) |
| 577 | 577 |
proto, containerPort := splitProtoPort(containerPort) |
| 578 | 578 |
if containerPort == "" {
|
| ... | ... |
@@ -597,7 +597,7 @@ func parsePortSpec(rawPort string) ([]container.PortMap, error) {
|
| 597 | 597 |
return nil, fmt.Errorf("invalid IP address: %w", err)
|
| 598 | 598 |
} |
| 599 | 599 |
|
| 600 |
- pr, err := container.ParsePortRange(containerPort) |
|
| 600 |
+ pr, err := network.ParsePortRange(containerPort) |
|
| 601 | 601 |
if err != nil {
|
| 602 | 602 |
return nil, errors.New("invalid containerPort: " + containerPort)
|
| 603 | 603 |
} |
| ... | ... |
@@ -609,7 +609,7 @@ func parsePortSpec(rawPort string) ([]container.PortMap, error) {
|
| 609 | 609 |
|
| 610 | 610 |
var startHostPort, endHostPort uint16 |
| 611 | 611 |
if hostPort != "" {
|
| 612 |
- hostPortRange, err := container.ParsePortRange(hostPort) |
|
| 612 |
+ hostPortRange, err := network.ParsePortRange(hostPort) |
|
| 613 | 613 |
if err != nil {
|
| 614 | 614 |
return nil, errors.New("invalid hostPort: " + hostPort)
|
| 615 | 615 |
} |
| ... | ... |
@@ -626,7 +626,7 @@ func parsePortSpec(rawPort string) ([]container.PortMap, error) {
|
| 626 | 626 |
} |
| 627 | 627 |
|
| 628 | 628 |
count := endPort - startPort + 1 |
| 629 |
- ports := make([]container.PortMap, 0, count) |
|
| 629 |
+ ports := make([]network.PortMap, 0, count) |
|
| 630 | 630 |
|
| 631 | 631 |
for i := uint16(0); i < count; i++ {
|
| 632 | 632 |
hPort := "" |
| ... | ... |
@@ -638,8 +638,8 @@ func parsePortSpec(rawPort string) ([]container.PortMap, error) {
|
| 638 | 638 |
hPort += "-" + strconv.Itoa(int(endHostPort)) |
| 639 | 639 |
} |
| 640 | 640 |
} |
| 641 |
- ports = append(ports, container.PortMap{
|
|
| 642 |
- container.MustParsePort(fmt.Sprintf("%d/%s", startPort+i, proto)): []container.PortBinding{{HostIP: addr, HostPort: hPort}},
|
|
| 641 |
+ ports = append(ports, network.PortMap{
|
|
| 642 |
+ network.MustParsePort(fmt.Sprintf("%d/%s", startPort+i, proto)): []network.PortBinding{{HostIP: addr, HostPort: hPort}},
|
|
| 643 | 643 |
}) |
| 644 | 644 |
} |
| 645 | 645 |
return ports, nil |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"github.com/moby/buildkit/frontend/dockerfile/parser" |
| 15 | 15 |
"github.com/moby/buildkit/frontend/dockerfile/shell" |
| 16 | 16 |
"github.com/moby/moby/api/types/container" |
| 17 |
+ "github.com/moby/moby/api/types/network" |
|
| 17 | 18 |
"github.com/moby/moby/v2/daemon/builder" |
| 18 | 19 |
"github.com/moby/moby/v2/daemon/internal/image" |
| 19 | 20 |
"github.com/moby/moby/v2/daemon/pkg/oci" |
| ... | ... |
@@ -337,7 +338,7 @@ func TestExpose(t *testing.T) {
|
| 337 | 337 |
assert.Assert(t, sb.state.runConfig.ExposedPorts != nil) |
| 338 | 338 |
assert.Assert(t, is.Len(sb.state.runConfig.ExposedPorts, 1)) |
| 339 | 339 |
|
| 340 |
- assert.Check(t, is.Contains(sb.state.runConfig.ExposedPorts, container.MustParsePort("80/tcp")))
|
|
| 340 |
+ assert.Check(t, is.Contains(sb.state.runConfig.ExposedPorts, network.MustParsePort("80/tcp")))
|
|
| 341 | 341 |
} |
| 342 | 342 |
|
| 343 | 343 |
func TestUser(t *testing.T) {
|
| ... | ... |
@@ -632,29 +633,29 @@ func TestDispatchUnsupportedOptions(t *testing.T) {
|
| 632 | 632 |
// Copied and modified from https://github.com/docker/go-connections/blob/c296721c0d56d3acad2973376ded214103a4fd2e/nat/nat_test.go#L390-L499 |
| 633 | 633 |
func TestParsePortSpecs(t *testing.T) {
|
| 634 | 634 |
var ( |
| 635 |
- portMap map[container.Port]struct{}
|
|
| 636 |
- bindingMap map[container.Port][]container.PortBinding |
|
| 635 |
+ portSet network.PortSet |
|
| 636 |
+ bindingMap network.PortMap |
|
| 637 | 637 |
err error |
| 638 | 638 |
) |
| 639 | 639 |
|
| 640 |
- tcp1234 := container.MustParsePort("1234/tcp")
|
|
| 641 |
- udp2345 := container.MustParsePort("2345/udp")
|
|
| 642 |
- sctp3456 := container.MustParsePort("3456/sctp")
|
|
| 640 |
+ tcp1234 := network.MustParsePort("1234/tcp")
|
|
| 641 |
+ udp2345 := network.MustParsePort("2345/udp")
|
|
| 642 |
+ sctp3456 := network.MustParsePort("3456/sctp")
|
|
| 643 | 643 |
|
| 644 |
- portMap, bindingMap, err = parsePortSpecs([]string{tcp1234.String(), udp2345.String(), sctp3456.String()})
|
|
| 644 |
+ portSet, bindingMap, err = parsePortSpecs([]string{tcp1234.String(), udp2345.String(), sctp3456.String()})
|
|
| 645 | 645 |
if err != nil {
|
| 646 | 646 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 647 | 647 |
} |
| 648 | 648 |
|
| 649 |
- if _, ok := portMap[tcp1234]; !ok {
|
|
| 649 |
+ if _, ok := portSet[tcp1234]; !ok {
|
|
| 650 | 650 |
t.Fatal("1234/tcp was not parsed properly")
|
| 651 | 651 |
} |
| 652 | 652 |
|
| 653 |
- if _, ok := portMap[udp2345]; !ok {
|
|
| 653 |
+ if _, ok := portSet[udp2345]; !ok {
|
|
| 654 | 654 |
t.Fatal("2345/udp was not parsed properly")
|
| 655 | 655 |
} |
| 656 | 656 |
|
| 657 |
- if _, ok := portMap[sctp3456]; !ok {
|
|
| 657 |
+ if _, ok := portSet[sctp3456]; !ok {
|
|
| 658 | 658 |
t.Fatal("3456/sctp was not parsed properly")
|
| 659 | 659 |
} |
| 660 | 660 |
|
| ... | ... |
@@ -672,20 +673,20 @@ func TestParsePortSpecs(t *testing.T) {
|
| 672 | 672 |
} |
| 673 | 673 |
} |
| 674 | 674 |
|
| 675 |
- portMap, bindingMap, err = parsePortSpecs([]string{"1234:1234/tcp", "2345:2345/udp", "3456:3456/sctp"})
|
|
| 675 |
+ portSet, bindingMap, err = parsePortSpecs([]string{"1234:1234/tcp", "2345:2345/udp", "3456:3456/sctp"})
|
|
| 676 | 676 |
if err != nil {
|
| 677 | 677 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 678 | 678 |
} |
| 679 | 679 |
|
| 680 |
- if _, ok := portMap[tcp1234]; !ok {
|
|
| 680 |
+ if _, ok := portSet[tcp1234]; !ok {
|
|
| 681 | 681 |
t.Fatal("1234/tcp was not parsed properly")
|
| 682 | 682 |
} |
| 683 | 683 |
|
| 684 |
- if _, ok := portMap[udp2345]; !ok {
|
|
| 684 |
+ if _, ok := portSet[udp2345]; !ok {
|
|
| 685 | 685 |
t.Fatal("2345/udp was not parsed properly")
|
| 686 | 686 |
} |
| 687 | 687 |
|
| 688 |
- if _, ok := portMap[sctp3456]; !ok {
|
|
| 688 |
+ if _, ok := portSet[sctp3456]; !ok {
|
|
| 689 | 689 |
t.Fatal("3456/sctp was not parsed properly")
|
| 690 | 690 |
} |
| 691 | 691 |
|
| ... | ... |
@@ -705,20 +706,20 @@ func TestParsePortSpecs(t *testing.T) {
|
| 705 | 705 |
} |
| 706 | 706 |
} |
| 707 | 707 |
|
| 708 |
- portMap, bindingMap, err = parsePortSpecs([]string{"0.0.0.0:1234:1234/tcp", "0.0.0.0:2345:2345/udp", "0.0.0.0:3456:3456/sctp"})
|
|
| 708 |
+ portSet, bindingMap, err = parsePortSpecs([]string{"0.0.0.0:1234:1234/tcp", "0.0.0.0:2345:2345/udp", "0.0.0.0:3456:3456/sctp"})
|
|
| 709 | 709 |
if err != nil {
|
| 710 | 710 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 711 | 711 |
} |
| 712 | 712 |
|
| 713 |
- if _, ok := portMap[tcp1234]; !ok {
|
|
| 713 |
+ if _, ok := portSet[tcp1234]; !ok {
|
|
| 714 | 714 |
t.Fatal("1234/tcp was not parsed properly")
|
| 715 | 715 |
} |
| 716 | 716 |
|
| 717 |
- if _, ok := portMap[udp2345]; !ok {
|
|
| 717 |
+ if _, ok := portSet[udp2345]; !ok {
|
|
| 718 | 718 |
t.Fatal("2345/udp was not parsed properly")
|
| 719 | 719 |
} |
| 720 | 720 |
|
| 721 |
- if _, ok := portMap[sctp3456]; !ok {
|
|
| 721 |
+ if _, ok := portSet[sctp3456]; !ok {
|
|
| 722 | 722 |
t.Fatal("3456/sctp was not parsed properly")
|
| 723 | 723 |
} |
| 724 | 724 |
|
| ... | ... |
@@ -784,9 +785,9 @@ func TestParsePortSpecFull(t *testing.T) {
|
| 784 | 784 |
t.Fatalf("expected nil error, got: %v", err)
|
| 785 | 785 |
} |
| 786 | 786 |
|
| 787 |
- expected := []container.PortMap{
|
|
| 787 |
+ expected := []network.PortMap{
|
|
| 788 | 788 |
{
|
| 789 |
- container.MustParsePort("3333/tcp"): []container.PortBinding{
|
|
| 789 |
+ network.MustParsePort("3333/tcp"): []network.PortBinding{
|
|
| 790 | 790 |
{
|
| 791 | 791 |
HostIP: netip.IPv4Unspecified(), |
| 792 | 792 |
HostPort: "1234", |
| ... | ... |
@@ -794,7 +795,7 @@ func TestParsePortSpecFull(t *testing.T) {
|
| 794 | 794 |
}, |
| 795 | 795 |
}, |
| 796 | 796 |
{
|
| 797 |
- container.MustParsePort("3334/tcp"): []container.PortBinding{
|
|
| 797 |
+ network.MustParsePort("3334/tcp"): []network.PortBinding{
|
|
| 798 | 798 |
{
|
| 799 | 799 |
HostIP: netip.IPv4Unspecified(), |
| 800 | 800 |
HostPort: "1235", |
| ... | ... |
@@ -813,15 +814,15 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 813 | 813 |
type test struct {
|
| 814 | 814 |
name string |
| 815 | 815 |
spec string |
| 816 |
- expected []container.PortMap |
|
| 816 |
+ expected []network.PortMap |
|
| 817 | 817 |
} |
| 818 | 818 |
cases := []test{
|
| 819 | 819 |
{
|
| 820 | 820 |
name: "square angled IPV6 without host port", |
| 821 | 821 |
spec: "[2001:4860:0:2001::68]::333", |
| 822 |
- expected: []container.PortMap{
|
|
| 822 |
+ expected: []network.PortMap{
|
|
| 823 | 823 |
{
|
| 824 |
- container.MustParsePort("333/tcp"): []container.PortBinding{
|
|
| 824 |
+ network.MustParsePort("333/tcp"): []network.PortBinding{
|
|
| 825 | 825 |
{
|
| 826 | 826 |
HostIP: netip.MustParseAddr("2001:4860:0:2001::68"),
|
| 827 | 827 |
HostPort: "", |
| ... | ... |
@@ -833,9 +834,9 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 833 | 833 |
{
|
| 834 | 834 |
name: "square angled IPV6 with host port", |
| 835 | 835 |
spec: "[::1]:80:80", |
| 836 |
- expected: []container.PortMap{
|
|
| 836 |
+ expected: []network.PortMap{
|
|
| 837 | 837 |
{
|
| 838 |
- container.MustParsePort("80/tcp"): []container.PortBinding{
|
|
| 838 |
+ network.MustParsePort("80/tcp"): []network.PortBinding{
|
|
| 839 | 839 |
{
|
| 840 | 840 |
HostIP: netip.IPv6Loopback(), |
| 841 | 841 |
HostPort: "80", |
| ... | ... |
@@ -847,9 +848,9 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 847 | 847 |
{
|
| 848 | 848 |
name: "IPV6 without host port", |
| 849 | 849 |
spec: "2001:4860:0:2001::68::333", |
| 850 |
- expected: []container.PortMap{
|
|
| 850 |
+ expected: []network.PortMap{
|
|
| 851 | 851 |
{
|
| 852 |
- container.MustParsePort("333/tcp"): []container.PortBinding{
|
|
| 852 |
+ network.MustParsePort("333/tcp"): []network.PortBinding{
|
|
| 853 | 853 |
{
|
| 854 | 854 |
HostIP: netip.MustParseAddr("2001:4860:0:2001::68"),
|
| 855 | 855 |
HostPort: "", |
| ... | ... |
@@ -861,9 +862,9 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 861 | 861 |
{
|
| 862 | 862 |
name: "IPV6 with host port", |
| 863 | 863 |
spec: "::1:80:80", |
| 864 |
- expected: []container.PortMap{
|
|
| 864 |
+ expected: []network.PortMap{
|
|
| 865 | 865 |
{
|
| 866 |
- container.MustParsePort("80/tcp"): []container.PortBinding{
|
|
| 866 |
+ network.MustParsePort("80/tcp"): []network.PortBinding{
|
|
| 867 | 867 |
{
|
| 868 | 868 |
HostIP: netip.IPv6Loopback(), |
| 869 | 869 |
HostPort: "80", |
| ... | ... |
@@ -875,9 +876,9 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 875 | 875 |
{
|
| 876 | 876 |
name: ":: IPV6, without host port", |
| 877 | 877 |
spec: "::::80", |
| 878 |
- expected: []container.PortMap{
|
|
| 878 |
+ expected: []network.PortMap{
|
|
| 879 | 879 |
{
|
| 880 |
- container.MustParsePort("80/tcp"): []container.PortBinding{
|
|
| 880 |
+ network.MustParsePort("80/tcp"): []network.PortBinding{
|
|
| 881 | 881 |
{
|
| 882 | 882 |
HostIP: netip.IPv6Unspecified(), |
| 883 | 883 |
HostPort: "", |
| ... | ... |
@@ -903,25 +904,25 @@ func TestPartPortSpecIPV6(t *testing.T) {
|
| 903 | 903 |
// Copied and modified from https://github.com/docker/go-connections/blob/c296721c0d56d3acad2973376ded214103a4fd2e/nat/nat_test.go#L501-L600 |
| 904 | 904 |
func TestParsePortSpecsWithRange(t *testing.T) {
|
| 905 | 905 |
var ( |
| 906 |
- portMap map[container.Port]struct{}
|
|
| 907 |
- bindingMap map[container.Port][]container.PortBinding |
|
| 906 |
+ portSet network.PortSet |
|
| 907 |
+ bindingMap network.PortMap |
|
| 908 | 908 |
err error |
| 909 | 909 |
) |
| 910 | 910 |
|
| 911 |
- portMap, bindingMap, err = parsePortSpecs([]string{"1234-1236/tcp", "2345-2347/udp", "3456-3458/sctp"})
|
|
| 911 |
+ portSet, bindingMap, err = parsePortSpecs([]string{"1234-1236/tcp", "2345-2347/udp", "3456-3458/sctp"})
|
|
| 912 | 912 |
if err != nil {
|
| 913 | 913 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 914 | 914 |
} |
| 915 | 915 |
|
| 916 |
- if _, ok := portMap[container.MustParsePort("1235/tcp")]; !ok {
|
|
| 916 |
+ if _, ok := portSet[network.MustParsePort("1235/tcp")]; !ok {
|
|
| 917 | 917 |
t.Fatal("1234-1236/tcp was not parsed properly")
|
| 918 | 918 |
} |
| 919 | 919 |
|
| 920 |
- if _, ok := portMap[container.MustParsePort("2346/udp")]; !ok {
|
|
| 920 |
+ if _, ok := portSet[network.MustParsePort("2346/udp")]; !ok {
|
|
| 921 | 921 |
t.Fatal("2345-2347/udp was not parsed properly")
|
| 922 | 922 |
} |
| 923 | 923 |
|
| 924 |
- if _, ok := portMap[container.MustParsePort("3456/sctp")]; !ok {
|
|
| 924 |
+ if _, ok := portSet[network.MustParsePort("3456/sctp")]; !ok {
|
|
| 925 | 925 |
t.Fatal("3456-3458/sctp was not parsed properly")
|
| 926 | 926 |
} |
| 927 | 927 |
|
| ... | ... |
@@ -939,20 +940,20 @@ func TestParsePortSpecsWithRange(t *testing.T) {
|
| 939 | 939 |
} |
| 940 | 940 |
} |
| 941 | 941 |
|
| 942 |
- portMap, bindingMap, err = parsePortSpecs([]string{"1234-1236:1234-1236/tcp", "2345-2347:2345-2347/udp", "3456-3458:3456-3458/sctp"})
|
|
| 942 |
+ portSet, bindingMap, err = parsePortSpecs([]string{"1234-1236:1234-1236/tcp", "2345-2347:2345-2347/udp", "3456-3458:3456-3458/sctp"})
|
|
| 943 | 943 |
if err != nil {
|
| 944 | 944 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 945 | 945 |
} |
| 946 | 946 |
|
| 947 |
- if _, ok := portMap[container.MustParsePort("1235/tcp")]; !ok {
|
|
| 947 |
+ if _, ok := portSet[network.MustParsePort("1235/tcp")]; !ok {
|
|
| 948 | 948 |
t.Fatal("1234-1236 was not parsed properly")
|
| 949 | 949 |
} |
| 950 | 950 |
|
| 951 |
- if _, ok := portMap[container.MustParsePort("2346/udp")]; !ok {
|
|
| 951 |
+ if _, ok := portSet[network.MustParsePort("2346/udp")]; !ok {
|
|
| 952 | 952 |
t.Fatal("2345-2347 was not parsed properly")
|
| 953 | 953 |
} |
| 954 | 954 |
|
| 955 |
- if _, ok := portMap[container.MustParsePort("3456/sctp")]; !ok {
|
|
| 955 |
+ if _, ok := portSet[network.MustParsePort("3456/sctp")]; !ok {
|
|
| 956 | 956 |
t.Fatal("3456-3458 was not parsed properly")
|
| 957 | 957 |
} |
| 958 | 958 |
|
| ... | ... |
@@ -971,20 +972,20 @@ func TestParsePortSpecsWithRange(t *testing.T) {
|
| 971 | 971 |
} |
| 972 | 972 |
} |
| 973 | 973 |
|
| 974 |
- portMap, bindingMap, err = parsePortSpecs([]string{"0.0.0.0:1234-1236:1234-1236/tcp", "0.0.0.0:2345-2347:2345-2347/udp", "0.0.0.0:3456-3458:3456-3458/sctp"})
|
|
| 974 |
+ portSet, bindingMap, err = parsePortSpecs([]string{"0.0.0.0:1234-1236:1234-1236/tcp", "0.0.0.0:2345-2347:2345-2347/udp", "0.0.0.0:3456-3458:3456-3458/sctp"})
|
|
| 975 | 975 |
if err != nil {
|
| 976 | 976 |
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
| 977 | 977 |
} |
| 978 | 978 |
|
| 979 |
- if _, ok := portMap[container.MustParsePort("1235/tcp")]; !ok {
|
|
| 979 |
+ if _, ok := portSet[network.MustParsePort("1235/tcp")]; !ok {
|
|
| 980 | 980 |
t.Fatal("1234-1236 was not parsed properly")
|
| 981 | 981 |
} |
| 982 | 982 |
|
| 983 |
- if _, ok := portMap[container.MustParsePort("2346/udp")]; !ok {
|
|
| 983 |
+ if _, ok := portSet[network.MustParsePort("2346/udp")]; !ok {
|
|
| 984 | 984 |
t.Fatal("2345-2347 was not parsed properly")
|
| 985 | 985 |
} |
| 986 | 986 |
|
| 987 |
- if _, ok := portMap[container.MustParsePort("3456/sctp")]; !ok {
|
|
| 987 |
+ if _, ok := portSet[network.MustParsePort("3456/sctp")]; !ok {
|
|
| 988 | 988 |
t.Fatal("3456-3458 was not parsed properly")
|
| 989 | 989 |
} |
| 990 | 990 |
|
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
"github.com/moby/go-archive" |
| 10 | 10 |
"github.com/moby/moby/api/types/container" |
| 11 |
+ "github.com/moby/moby/api/types/network" |
|
| 11 | 12 |
"github.com/moby/moby/v2/daemon/builder" |
| 12 | 13 |
"github.com/moby/moby/v2/daemon/builder/remotecontext" |
| 13 | 14 |
"github.com/moby/moby/v2/daemon/internal/image" |
| ... | ... |
@@ -135,9 +136,9 @@ func fullMutableRunConfig() *container.Config {
|
| 135 | 135 |
return &container.Config{
|
| 136 | 136 |
Cmd: []string{"command", "arg1"},
|
| 137 | 137 |
Env: []string{"env1=foo", "env2=bar"},
|
| 138 |
- ExposedPorts: container.PortSet{
|
|
| 139 |
- container.MustParsePort("1000/tcp"): {},
|
|
| 140 |
- container.MustParsePort("1001/tcp"): {},
|
|
| 138 |
+ ExposedPorts: network.PortSet{
|
|
| 139 |
+ network.MustParsePort("1000/tcp"): {},
|
|
| 140 |
+ network.MustParsePort("1001/tcp"): {},
|
|
| 141 | 141 |
}, |
| 142 | 142 |
Volumes: map[string]struct{}{
|
| 143 | 143 |
"one": {},
|
| ... | ... |
@@ -160,7 +161,7 @@ func TestDeepCopyRunConfig(t *testing.T) {
|
| 160 | 160 |
|
| 161 | 161 |
ctrCfg.Cmd[1] = "arg2" |
| 162 | 162 |
ctrCfg.Env[1] = "env2=new" |
| 163 |
- ctrCfg.ExposedPorts[container.MustParsePort("10002")] = struct{}{}
|
|
| 163 |
+ ctrCfg.ExposedPorts[network.MustParsePort("10002")] = struct{}{}
|
|
| 164 | 164 |
ctrCfg.Volumes["three"] = struct{}{}
|
| 165 | 165 |
ctrCfg.Entrypoint[1] = "arg2" |
| 166 | 166 |
ctrCfg.OnBuild[0] = "start" |
| ... | ... |
@@ -143,8 +143,8 @@ func (c *containerConfig) image() string {
|
| 143 | 143 |
return reference.FamiliarString(reference.TagNameOnly(ref)) |
| 144 | 144 |
} |
| 145 | 145 |
|
| 146 |
-func (c *containerConfig) portBindings() container.PortMap {
|
|
| 147 |
- portBindings := container.PortMap{}
|
|
| 146 |
+func (c *containerConfig) portBindings() network.PortMap {
|
|
| 147 |
+ portBindings := network.PortMap{}
|
|
| 148 | 148 |
if c.task.Endpoint == nil {
|
| 149 | 149 |
return portBindings |
| 150 | 150 |
} |
| ... | ... |
@@ -158,12 +158,12 @@ func (c *containerConfig) portBindings() container.PortMap {
|
| 158 | 158 |
continue |
| 159 | 159 |
} |
| 160 | 160 |
|
| 161 |
- port, ok := container.PortFrom(uint16(portConfig.TargetPort), container.NetworkProtocol(portConfig.Protocol.String())) |
|
| 161 |
+ port, ok := network.PortFrom(uint16(portConfig.TargetPort), network.IPProtocol(portConfig.Protocol.String())) |
|
| 162 | 162 |
if !ok {
|
| 163 | 163 |
continue |
| 164 | 164 |
} |
| 165 | 165 |
|
| 166 |
- binding := []container.PortBinding{
|
|
| 166 |
+ binding := []network.PortBinding{
|
|
| 167 | 167 |
{},
|
| 168 | 168 |
} |
| 169 | 169 |
|
| ... | ... |
@@ -188,8 +188,8 @@ func (c *containerConfig) init() *bool {
|
| 188 | 188 |
return &init |
| 189 | 189 |
} |
| 190 | 190 |
|
| 191 |
-func (c *containerConfig) exposedPorts() map[container.Port]struct{} {
|
|
| 192 |
- exposedPorts := make(map[container.Port]struct{})
|
|
| 191 |
+func (c *containerConfig) exposedPorts() map[network.Port]struct{} {
|
|
| 192 |
+ exposedPorts := make(map[network.Port]struct{})
|
|
| 193 | 193 |
if c.task.Endpoint == nil {
|
| 194 | 194 |
return exposedPorts |
| 195 | 195 |
} |
| ... | ... |
@@ -203,7 +203,7 @@ func (c *containerConfig) exposedPorts() map[container.Port]struct{} {
|
| 203 | 203 |
continue |
| 204 | 204 |
} |
| 205 | 205 |
|
| 206 |
- port, ok := container.PortFrom(uint16(portConfig.TargetPort), container.NetworkProtocol(portConfig.Protocol.String())) |
|
| 206 |
+ port, ok := network.PortFrom(uint16(portConfig.TargetPort), network.IPProtocol(portConfig.Protocol.String())) |
|
| 207 | 207 |
if !ok {
|
| 208 | 208 |
continue |
| 209 | 209 |
} |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
gogotypes "github.com/gogo/protobuf/types" |
| 12 | 12 |
"github.com/moby/moby/api/types/container" |
| 13 | 13 |
"github.com/moby/moby/api/types/events" |
| 14 |
+ "github.com/moby/moby/api/types/network" |
|
| 14 | 15 |
executorpkg "github.com/moby/moby/v2/daemon/cluster/executor" |
| 15 | 16 |
"github.com/moby/moby/v2/daemon/libnetwork" |
| 16 | 17 |
"github.com/moby/swarmkit/v2/agent/exec" |
| ... | ... |
@@ -639,17 +640,17 @@ func parsePortStatus(ctnr container.InspectResponse) (*api.PortStatus, error) {
|
| 639 | 639 |
return status, nil |
| 640 | 640 |
} |
| 641 | 641 |
|
| 642 |
-func parsePortMap(portMap container.PortMap) ([]*api.PortConfig, error) {
|
|
| 642 |
+func parsePortMap(portMap network.PortMap) ([]*api.PortConfig, error) {
|
|
| 643 | 643 |
exposedPorts := make([]*api.PortConfig, 0, len(portMap)) |
| 644 | 644 |
|
| 645 | 645 |
for port, mapping := range portMap {
|
| 646 | 646 |
var protocol api.PortConfig_Protocol |
| 647 | 647 |
switch port.Proto() {
|
| 648 |
- case container.TCP: |
|
| 648 |
+ case network.TCP: |
|
| 649 | 649 |
protocol = api.ProtocolTCP |
| 650 |
- case container.UDP: |
|
| 650 |
+ case network.UDP: |
|
| 651 | 651 |
protocol = api.ProtocolUDP |
| 652 |
- case container.SCTP: |
|
| 652 |
+ case network.SCTP: |
|
| 653 | 653 |
protocol = api.ProtocolSCTP |
| 654 | 654 |
default: |
| 655 | 655 |
return nil, fmt.Errorf("invalid protocol: %s", port.Proto())
|
| ... | ... |
@@ -372,7 +372,7 @@ func validateHealthCheck(healthConfig *containertypes.HealthConfig) error {
|
| 372 | 372 |
return nil |
| 373 | 373 |
} |
| 374 | 374 |
|
| 375 |
-func validatePortBindings(ports containertypes.PortMap) error {
|
|
| 375 |
+func validatePortBindings(ports networktypes.PortMap) error {
|
|
| 376 | 376 |
for port := range ports {
|
| 377 | 377 |
if !port.IsValid() {
|
| 378 | 378 |
return errors.Errorf("invalid port specification: %q", port.String())
|
| ... | ... |
@@ -384,7 +384,7 @@ func validatePortBindings(ports containertypes.PortMap) error {
|
| 384 | 384 |
continue |
| 385 | 385 |
} |
| 386 | 386 |
|
| 387 |
- if _, err := containertypes.ParsePortRange(pb.HostPort); err != nil {
|
|
| 387 |
+ if _, err := networktypes.ParsePortRange(pb.HostPort); err != nil {
|
|
| 388 | 388 |
return errors.Errorf("invalid port specification: %q", pb.HostPort)
|
| 389 | 389 |
} |
| 390 | 390 |
} |
| ... | ... |
@@ -22,6 +22,7 @@ import ( |
| 22 | 22 |
containertypes "github.com/moby/moby/api/types/container" |
| 23 | 23 |
"github.com/moby/moby/api/types/events" |
| 24 | 24 |
mounttypes "github.com/moby/moby/api/types/mount" |
| 25 |
+ networktypes "github.com/moby/moby/api/types/network" |
|
| 25 | 26 |
swarmtypes "github.com/moby/moby/api/types/swarm" |
| 26 | 27 |
"github.com/moby/moby/v2/daemon/internal/image" |
| 27 | 28 |
libcontainerdtypes "github.com/moby/moby/v2/daemon/internal/libcontainerd/types" |
| ... | ... |
@@ -651,7 +652,7 @@ func (container *Container) BackfillEmptyPBs() {
|
| 651 | 651 |
if len(pb) > 0 || pb == nil {
|
| 652 | 652 |
continue |
| 653 | 653 |
} |
| 654 |
- container.HostConfig.PortBindings[portProto] = []containertypes.PortBinding{
|
|
| 654 |
+ container.HostConfig.PortBindings[portProto] = []networktypes.PortBinding{
|
|
| 655 | 655 |
{}, // Backfill an empty PortBinding
|
| 656 | 656 |
} |
| 657 | 657 |
} |
| ... | ... |
@@ -39,8 +39,8 @@ type Snapshot struct {
|
| 39 | 39 |
Running bool |
| 40 | 40 |
Paused bool |
| 41 | 41 |
Managed bool |
| 42 |
- ExposedPorts container.PortSet |
|
| 43 |
- PortBindings container.PortSet |
|
| 42 |
+ ExposedPorts network.PortSet |
|
| 43 |
+ PortBindings network.PortSet |
|
| 44 | 44 |
Health container.HealthStatus |
| 45 | 45 |
HostConfig struct {
|
| 46 | 46 |
Isolation string |
| ... | ... |
@@ -318,8 +318,8 @@ func (v *View) transform(ctr *Container) *Snapshot {
|
| 318 | 318 |
Name: ctr.Name, |
| 319 | 319 |
Pid: ctr.State.Pid, |
| 320 | 320 |
Managed: ctr.Managed, |
| 321 |
- ExposedPorts: make(container.PortSet), |
|
| 322 |
- PortBindings: make(container.PortSet), |
|
| 321 |
+ ExposedPorts: make(network.PortSet), |
|
| 322 |
+ PortBindings: make(network.PortSet), |
|
| 323 | 323 |
Health: health, |
| 324 | 324 |
Running: ctr.State.Running, |
| 325 | 325 |
Paused: ctr.State.Paused, |
| ... | ... |
@@ -398,8 +398,8 @@ func (v *View) transform(ctr *Container) *Snapshot {
|
| 398 | 398 |
continue |
| 399 | 399 |
} |
| 400 | 400 |
for _, binding := range bindings {
|
| 401 |
- // TODO(thaJeztah): if this is always a port/proto (no range), we can simplify this to [container.ParsePort]. |
|
| 402 |
- h, err := container.ParsePortRange(binding.HostPort) |
|
| 401 |
+ // TODO(thaJeztah): if this is always a port/proto (no range), we can simplify this to [network.ParsePort]. |
|
| 402 |
+ h, err := network.ParsePortRange(binding.HostPort) |
|
| 403 | 403 |
if err != nil {
|
| 404 | 404 |
log.G(context.TODO()).WithError(err).Warn("invalid host port map")
|
| 405 | 405 |
continue |
| ... | ... |
@@ -94,7 +94,7 @@ func buildSandboxOptions(cfg *config.Config, ctr *container.Container) ([]libnet |
| 94 | 94 |
} |
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 |
- portBindings := make(containertypes.PortMap, len(ctr.HostConfig.PortBindings)) |
|
| 97 |
+ portBindings := make(networktypes.PortMap, len(ctr.HostConfig.PortBindings)) |
|
| 98 | 98 |
for p, b := range ctr.HostConfig.PortBindings {
|
| 99 | 99 |
portBindings[p] = slices.Clone(b) |
| 100 | 100 |
} |
| ... | ... |
@@ -119,13 +119,13 @@ func buildSandboxOptions(cfg *config.Config, ctr *container.Container) ([]libnet |
| 119 | 119 |
|
| 120 | 120 |
for _, binding := range bindings {
|
| 121 | 121 |
var ( |
| 122 |
- portRange containertypes.PortRange |
|
| 122 |
+ portRange networktypes.PortRange |
|
| 123 | 123 |
err error |
| 124 | 124 |
) |
| 125 | 125 |
|
| 126 | 126 |
// Empty HostPort means to map to an ephemeral port. |
| 127 | 127 |
if binding.HostPort != "" {
|
| 128 |
- portRange, err = containertypes.ParsePortRange(binding.HostPort) |
|
| 128 |
+ portRange, err = networktypes.ParsePortRange(binding.HostPort) |
|
| 129 | 129 |
if err != nil {
|
| 130 | 130 |
return nil, fmt.Errorf("error parsing HostPort value(%s):%v", binding.HostPort, err)
|
| 131 | 131 |
} |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 | 8 |
containertypes "github.com/moby/moby/api/types/container" |
| 9 |
+ "github.com/moby/moby/api/types/network" |
|
| 9 | 10 |
"github.com/moby/moby/v2/daemon/config" |
| 10 | 11 |
"gotest.tools/v3/assert" |
| 11 | 12 |
) |
| ... | ... |
@@ -14,12 +15,12 @@ import ( |
| 14 | 14 |
// This should not be tested on Windows because Windows doesn't support "host" network mode. |
| 15 | 15 |
func TestContainerWarningHostAndPublishPorts(t *testing.T) {
|
| 16 | 16 |
testCases := []struct {
|
| 17 |
- ports containertypes.PortMap |
|
| 17 |
+ ports network.PortMap |
|
| 18 | 18 |
warnings []string |
| 19 | 19 |
}{
|
| 20 |
- {ports: containertypes.PortMap{}},
|
|
| 21 |
- {ports: containertypes.PortMap{
|
|
| 22 |
- containertypes.MustParsePort("8080"): []containertypes.PortBinding{{HostPort: "8989"}},
|
|
| 20 |
+ {ports: network.PortMap{}},
|
|
| 21 |
+ {ports: network.PortMap{
|
|
| 22 |
+ network.MustParsePort("8080"): []network.PortBinding{{HostPort: "8989"}},
|
|
| 23 | 23 |
}, warnings: []string{"Published ports are discarded when using host network mode"}},
|
| 24 | 24 |
} |
| 25 | 25 |
muteLogs(t) |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"testing" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/moby/moby/api/types/container" |
| 7 |
+ "github.com/moby/moby/api/types/network" |
|
| 7 | 8 |
"gotest.tools/v3/assert" |
| 8 | 9 |
is "gotest.tools/v3/assert/cmp" |
| 9 | 10 |
) |
| ... | ... |
@@ -11,8 +12,8 @@ import ( |
| 11 | 11 |
// regression test for https://github.com/moby/moby/issues/45904 |
| 12 | 12 |
func TestContainerConfigToDockerImageConfig(t *testing.T) {
|
| 13 | 13 |
ociCFG := containerConfigToDockerOCIImageConfig(&container.Config{
|
| 14 |
- ExposedPorts: container.PortSet{
|
|
| 15 |
- container.MustParsePort("80/tcp"): struct{}{},
|
|
| 14 |
+ ExposedPorts: network.PortSet{
|
|
| 15 |
+ network.MustParsePort("80/tcp"): struct{}{},
|
|
| 16 | 16 |
}, |
| 17 | 17 |
}) |
| 18 | 18 |
|
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
|
| 6 | 6 |
imagespec "github.com/moby/docker-image-spec/specs-go/v1" |
| 7 | 7 |
"github.com/moby/moby/api/types/container" |
| 8 |
+ "github.com/moby/moby/api/types/network" |
|
| 8 | 9 |
"github.com/moby/moby/v2/daemon/internal/image" |
| 9 | 10 |
"github.com/moby/moby/v2/dockerversion" |
| 10 | 11 |
ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| ... | ... |
@@ -95,9 +96,9 @@ func containerConfigToDockerOCIImageConfig(cfg *container.Config) imagespec.Dock |
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 | 97 |
func dockerOCIImageConfigToContainerConfig(cfg imagespec.DockerOCIImageConfig) *container.Config {
|
| 98 |
- exposedPorts := make(container.PortSet, len(cfg.ExposedPorts)) |
|
| 98 |
+ exposedPorts := make(network.PortSet, len(cfg.ExposedPorts)) |
|
| 99 | 99 |
for k := range cfg.ExposedPorts {
|
| 100 |
- if p, err := container.ParsePort(k); err == nil {
|
|
| 100 |
+ if p, err := network.ParsePort(k); err == nil {
|
|
| 101 | 101 |
exposedPorts[p] = struct{}{}
|
| 102 | 102 |
} |
| 103 | 103 |
} |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
cerrdefs "github.com/containerd/errdefs" |
| 13 | 13 |
containertypes "github.com/moby/moby/api/types/container" |
| 14 |
+ "github.com/moby/moby/api/types/network" |
|
| 14 | 15 |
"github.com/moby/moby/v2/daemon/container" |
| 15 | 16 |
"github.com/moby/moby/v2/daemon/internal/idtools" |
| 16 | 17 |
"github.com/moby/moby/v2/daemon/libnetwork" |
| ... | ... |
@@ -220,9 +221,9 @@ func TestContainerInitDNS(t *testing.T) {
|
| 220 | 220 |
|
| 221 | 221 |
func TestMerge(t *testing.T) {
|
| 222 | 222 |
configImage := &containertypes.Config{
|
| 223 |
- ExposedPorts: containertypes.PortSet{
|
|
| 224 |
- containertypes.MustParsePort("1111/tcp"): struct{}{},
|
|
| 225 |
- containertypes.MustParsePort("2222/tcp"): struct{}{},
|
|
| 223 |
+ ExposedPorts: network.PortSet{
|
|
| 224 |
+ network.MustParsePort("1111/tcp"): struct{}{},
|
|
| 225 |
+ network.MustParsePort("2222/tcp"): struct{}{},
|
|
| 226 | 226 |
}, |
| 227 | 227 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 228 | 228 |
Volumes: map[string]struct{}{
|
| ... | ... |
@@ -232,9 +233,9 @@ func TestMerge(t *testing.T) {
|
| 232 | 232 |
} |
| 233 | 233 |
|
| 234 | 234 |
configUser := &containertypes.Config{
|
| 235 |
- ExposedPorts: containertypes.PortSet{
|
|
| 236 |
- containertypes.MustParsePort("2222/tcp"): struct{}{},
|
|
| 237 |
- containertypes.MustParsePort("3333/tcp"): struct{}{},
|
|
| 235 |
+ ExposedPorts: network.PortSet{
|
|
| 236 |
+ network.MustParsePort("2222/tcp"): struct{}{},
|
|
| 237 |
+ network.MustParsePort("3333/tcp"): struct{}{},
|
|
| 238 | 238 |
}, |
| 239 | 239 |
Env: []string{"VAR2=3", "VAR3=3"},
|
| 240 | 240 |
Volumes: map[string]struct{}{
|
| ... | ... |
@@ -273,8 +274,8 @@ func TestMerge(t *testing.T) {
|
| 273 | 273 |
} |
| 274 | 274 |
|
| 275 | 275 |
configImage2 := &containertypes.Config{
|
| 276 |
- ExposedPorts: map[containertypes.Port]struct{}{
|
|
| 277 |
- containertypes.MustParsePort("0/tcp"): {},
|
|
| 276 |
+ ExposedPorts: map[network.Port]struct{}{
|
|
| 277 |
+ network.MustParsePort("0/tcp"): {},
|
|
| 278 | 278 |
}, |
| 279 | 279 |
} |
| 280 | 280 |
|
| ... | ... |
@@ -34,7 +34,7 @@ func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, options |
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 | 36 |
// TODO(thaJeztah): do we need a deep copy here? Otherwise we could use maps.Clone (see https://github.com/moby/moby/commit/7917a36cc787ada58987320e67cc6d96858f3b55) |
| 37 |
- ports := make(containertypes.PortMap, len(ctr.NetworkSettings.Ports)) |
|
| 37 |
+ ports := make(networktypes.PortMap, len(ctr.NetworkSettings.Ports)) |
|
| 38 | 38 |
for k, pm := range ctr.NetworkSettings.Ports {
|
| 39 | 39 |
ports[k] = pm |
| 40 | 40 |
} |
| ... | ... |
@@ -5,24 +5,25 @@ import ( |
| 5 | 5 |
"testing" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/moby/moby/api/types/container" |
| 8 |
+ "github.com/moby/moby/api/types/network" |
|
| 8 | 9 |
ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| 9 | 10 |
"gotest.tools/v3/assert" |
| 10 | 11 |
is "gotest.tools/v3/assert/cmp" |
| 11 | 12 |
) |
| 12 | 13 |
|
| 13 | 14 |
func TestCompare(t *testing.T) {
|
| 14 |
- ports1 := container.PortSet{
|
|
| 15 |
- container.MustParsePort("1111/tcp"): struct{}{},
|
|
| 16 |
- container.MustParsePort("2222/tcp"): struct{}{},
|
|
| 15 |
+ ports1 := network.PortSet{
|
|
| 16 |
+ network.MustParsePort("1111/tcp"): struct{}{},
|
|
| 17 |
+ network.MustParsePort("2222/tcp"): struct{}{},
|
|
| 17 | 18 |
} |
| 18 |
- ports2 := container.PortSet{
|
|
| 19 |
- container.MustParsePort("3333/tcp"): struct{}{},
|
|
| 20 |
- container.MustParsePort("4444/tcp"): struct{}{},
|
|
| 19 |
+ ports2 := network.PortSet{
|
|
| 20 |
+ network.MustParsePort("3333/tcp"): struct{}{},
|
|
| 21 |
+ network.MustParsePort("4444/tcp"): struct{}{},
|
|
| 21 | 22 |
} |
| 22 |
- ports3 := container.PortSet{
|
|
| 23 |
- container.MustParsePort("1111/tcp"): struct{}{},
|
|
| 24 |
- container.MustParsePort("2222/tcp"): struct{}{},
|
|
| 25 |
- container.MustParsePort("5555/tcp"): struct{}{},
|
|
| 23 |
+ ports3 := network.PortSet{
|
|
| 24 |
+ network.MustParsePort("1111/tcp"): struct{}{},
|
|
| 25 |
+ network.MustParsePort("2222/tcp"): struct{}{},
|
|
| 26 |
+ network.MustParsePort("5555/tcp"): struct{}{},
|
|
| 26 | 27 |
} |
| 27 | 28 |
volumes1 := map[string]struct{}{
|
| 28 | 29 |
"/test1": {},
|
| ... | ... |
@@ -8,7 +8,7 @@ import ( |
| 8 | 8 |
"slices" |
| 9 | 9 |
"strings" |
| 10 | 10 |
|
| 11 |
- "github.com/moby/moby/api/types/container" |
|
| 11 |
+ "github.com/moby/moby/api/types/network" |
|
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
// Link struct holds information about parent/child linked container |
| ... | ... |
@@ -22,17 +22,17 @@ type Link struct {
|
| 22 | 22 |
// Child environments variables |
| 23 | 23 |
ChildEnvironment []string |
| 24 | 24 |
// Child exposed ports |
| 25 |
- Ports []container.Port |
|
| 25 |
+ Ports []network.Port |
|
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 | 28 |
// EnvVars generates environment variables for the linked container |
| 29 | 29 |
// for the Link with the given options. |
| 30 |
-func EnvVars(parentIP, childIP, name string, env []string, exposedPorts map[container.Port]struct{}) []string {
|
|
| 30 |
+func EnvVars(parentIP, childIP, name string, env []string, exposedPorts map[network.Port]struct{}) []string {
|
|
| 31 | 31 |
return NewLink(parentIP, childIP, name, env, exposedPorts).ToEnv() |
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 | 34 |
// NewLink initializes a new Link struct with the provided options. |
| 35 |
-func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[container.Port]struct{}) *Link {
|
|
| 35 |
+func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[network.Port]struct{}) *Link {
|
|
| 36 | 36 |
ports := slices.Collect(maps.Keys(exposedPorts)) |
| 37 | 37 |
|
| 38 | 38 |
return &Link{
|
| ... | ... |
@@ -55,7 +55,7 @@ func (l *Link) ToEnv() []string {
|
| 55 | 55 |
slices.SortFunc(l.Ports, withTCPPriority) |
| 56 | 56 |
|
| 57 | 57 |
env := make([]string, 0, 1+len(l.Ports)*4) |
| 58 |
- var pStart, pEnd container.Port |
|
| 58 |
+ var pStart, pEnd network.Port |
|
| 59 | 59 |
|
| 60 | 60 |
for i, p := range l.Ports {
|
| 61 | 61 |
if i == 0 {
|
| ... | ... |
@@ -111,14 +111,14 @@ func (l *Link) ToEnv() []string {
|
| 111 | 111 |
|
| 112 | 112 |
// withTCPPriority prioritizes ports using TCP over other protocols before |
| 113 | 113 |
// comparing port-number and protocol. |
| 114 |
-func withTCPPriority(ip, jp container.Port) int {
|
|
| 114 |
+func withTCPPriority(ip, jp network.Port) int {
|
|
| 115 | 115 |
if ip.Proto() == jp.Proto() {
|
| 116 | 116 |
return cmp.Compare(ip.Num(), jp.Num()) |
| 117 | 117 |
} |
| 118 |
- if ip.Proto() == container.TCP {
|
|
| 118 |
+ if ip.Proto() == network.TCP {
|
|
| 119 | 119 |
return -1 |
| 120 | 120 |
} |
| 121 |
- if jp.Proto() == container.TCP {
|
|
| 121 |
+ if jp.Proto() == network.TCP {
|
|
| 122 | 122 |
return 1 |
| 123 | 123 |
} |
| 124 | 124 |
return cmp.Compare(ip.Proto(), jp.Proto()) |
| ... | ... |
@@ -6,13 +6,13 @@ import ( |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 | 8 |
"github.com/google/go-cmp/cmp/cmpopts" |
| 9 |
- "github.com/moby/moby/api/types/container" |
|
| 9 |
+ "github.com/moby/moby/api/types/network" |
|
| 10 | 10 |
"gotest.tools/v3/assert" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
func TestLinkNaming(t *testing.T) {
|
| 14 |
- actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker-1", nil, container.PortSet{
|
|
| 15 |
- container.MustParsePort("6379/tcp"): struct{}{},
|
|
| 14 |
+ actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker-1", nil, network.PortSet{
|
|
| 15 |
+ network.MustParsePort("6379/tcp"): struct{}{},
|
|
| 16 | 16 |
}) |
| 17 | 17 |
|
| 18 | 18 |
expectedEnv := []string{
|
| ... | ... |
@@ -29,8 +29,8 @@ func TestLinkNaming(t *testing.T) {
|
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 | 31 |
func TestLinkNew(t *testing.T) {
|
| 32 |
- tcp6379 := container.MustParsePort("6379/tcp")
|
|
| 33 |
- link := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", nil, container.PortSet{
|
|
| 32 |
+ tcp6379 := network.MustParsePort("6379/tcp")
|
|
| 33 |
+ link := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", nil, network.PortSet{
|
|
| 34 | 34 |
tcp6379: struct{}{},
|
| 35 | 35 |
}) |
| 36 | 36 |
|
| ... | ... |
@@ -38,15 +38,15 @@ func TestLinkNew(t *testing.T) {
|
| 38 | 38 |
Name: "/db/docker", |
| 39 | 39 |
ParentIP: "172.0.17.3", |
| 40 | 40 |
ChildIP: "172.0.17.2", |
| 41 |
- Ports: []container.Port{tcp6379},
|
|
| 41 |
+ Ports: []network.Port{tcp6379},
|
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 |
- assert.DeepEqual(t, expected, link, cmpopts.EquateComparable(container.Port{}))
|
|
| 44 |
+ assert.DeepEqual(t, expected, link, cmpopts.EquateComparable(network.Port{}))
|
|
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 | 47 |
func TestLinkEnv(t *testing.T) {
|
| 48 |
- tcp6379 := container.MustParsePort("6379/tcp")
|
|
| 49 |
- actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, container.PortSet{
|
|
| 48 |
+ tcp6379 := network.MustParsePort("6379/tcp")
|
|
| 49 |
+ actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, network.PortSet{
|
|
| 50 | 50 |
tcp6379: struct{}{},
|
| 51 | 51 |
}) |
| 52 | 52 |
|
| ... | ... |
@@ -67,39 +67,39 @@ func TestLinkEnv(t *testing.T) {
|
| 67 | 67 |
// TestSortPorts verifies that ports are sorted with TCP taking priority, |
| 68 | 68 |
// and ports with the same protocol to be sorted by port. |
| 69 | 69 |
func TestSortPorts(t *testing.T) {
|
| 70 |
- ports := []container.Port{
|
|
| 71 |
- container.MustParsePort("6379/tcp"),
|
|
| 72 |
- container.MustParsePort("6376/udp"),
|
|
| 73 |
- container.MustParsePort("6380/tcp"),
|
|
| 74 |
- container.MustParsePort("6376/sctp"),
|
|
| 75 |
- container.MustParsePort("6381/tcp"),
|
|
| 76 |
- container.MustParsePort("6381/udp"),
|
|
| 77 |
- container.MustParsePort("6375/udp"),
|
|
| 78 |
- container.MustParsePort("6375/sctp"),
|
|
| 70 |
+ ports := []network.Port{
|
|
| 71 |
+ network.MustParsePort("6379/tcp"),
|
|
| 72 |
+ network.MustParsePort("6376/udp"),
|
|
| 73 |
+ network.MustParsePort("6380/tcp"),
|
|
| 74 |
+ network.MustParsePort("6376/sctp"),
|
|
| 75 |
+ network.MustParsePort("6381/tcp"),
|
|
| 76 |
+ network.MustParsePort("6381/udp"),
|
|
| 77 |
+ network.MustParsePort("6375/udp"),
|
|
| 78 |
+ network.MustParsePort("6375/sctp"),
|
|
| 79 | 79 |
} |
| 80 | 80 |
|
| 81 |
- expected := []container.Port{
|
|
| 82 |
- container.MustParsePort("6379/tcp"),
|
|
| 83 |
- container.MustParsePort("6380/tcp"),
|
|
| 84 |
- container.MustParsePort("6381/tcp"),
|
|
| 85 |
- container.MustParsePort("6375/sctp"),
|
|
| 86 |
- container.MustParsePort("6376/sctp"),
|
|
| 87 |
- container.MustParsePort("6375/udp"),
|
|
| 88 |
- container.MustParsePort("6376/udp"),
|
|
| 89 |
- container.MustParsePort("6381/udp"),
|
|
| 81 |
+ expected := []network.Port{
|
|
| 82 |
+ network.MustParsePort("6379/tcp"),
|
|
| 83 |
+ network.MustParsePort("6380/tcp"),
|
|
| 84 |
+ network.MustParsePort("6381/tcp"),
|
|
| 85 |
+ network.MustParsePort("6375/sctp"),
|
|
| 86 |
+ network.MustParsePort("6376/sctp"),
|
|
| 87 |
+ network.MustParsePort("6375/udp"),
|
|
| 88 |
+ network.MustParsePort("6376/udp"),
|
|
| 89 |
+ network.MustParsePort("6381/udp"),
|
|
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 | 92 |
slices.SortFunc(ports, withTCPPriority) |
| 93 |
- assert.DeepEqual(t, expected, ports, cmpopts.EquateComparable(container.Port{}))
|
|
| 93 |
+ assert.DeepEqual(t, expected, ports, cmpopts.EquateComparable(network.Port{}))
|
|
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 | 96 |
func TestLinkMultipleEnv(t *testing.T) {
|
| 97 |
- actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, container.PortSet{
|
|
| 98 |
- container.MustParsePort("6300/udp"): struct{}{},
|
|
| 99 |
- container.MustParsePort("6379/tcp"): struct{}{},
|
|
| 100 |
- container.MustParsePort("6380/tcp"): struct{}{},
|
|
| 101 |
- container.MustParsePort("6381/tcp"): struct{}{},
|
|
| 102 |
- container.MustParsePort("6382/udp"): struct{}{},
|
|
| 97 |
+ actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, network.PortSet{
|
|
| 98 |
+ network.MustParsePort("6300/udp"): struct{}{},
|
|
| 99 |
+ network.MustParsePort("6379/tcp"): struct{}{},
|
|
| 100 |
+ network.MustParsePort("6380/tcp"): struct{}{},
|
|
| 101 |
+ network.MustParsePort("6381/tcp"): struct{}{},
|
|
| 102 |
+ network.MustParsePort("6382/udp"): struct{}{},
|
|
| 103 | 103 |
}) |
| 104 | 104 |
|
| 105 | 105 |
expectedEnv := []string{
|
| ... | ... |
@@ -145,12 +145,12 @@ func BenchmarkLinkMultipleEnv(b *testing.B) {
|
| 145 | 145 |
b.ReportAllocs() |
| 146 | 146 |
b.ResetTimer() |
| 147 | 147 |
for i := 0; i < b.N; i++ {
|
| 148 |
- _ = EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, container.PortSet{
|
|
| 149 |
- container.MustParsePort("6300/udp"): struct{}{},
|
|
| 150 |
- container.MustParsePort("6379/tcp"): struct{}{},
|
|
| 151 |
- container.MustParsePort("6380/tcp"): struct{}{},
|
|
| 152 |
- container.MustParsePort("6381/tcp"): struct{}{},
|
|
| 153 |
- container.MustParsePort("6382/udp"): struct{}{},
|
|
| 148 |
+ _ = EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, network.PortSet{
|
|
| 149 |
+ network.MustParsePort("6300/udp"): struct{}{},
|
|
| 150 |
+ network.MustParsePort("6379/tcp"): struct{}{},
|
|
| 151 |
+ network.MustParsePort("6380/tcp"): struct{}{},
|
|
| 152 |
+ network.MustParsePort("6381/tcp"): struct{}{},
|
|
| 153 |
+ network.MustParsePort("6382/udp"): struct{}{},
|
|
| 154 | 154 |
}) |
| 155 | 155 |
} |
| 156 | 156 |
} |
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"github.com/containerd/log" |
| 14 | 14 |
containertypes "github.com/moby/moby/api/types/container" |
| 15 | 15 |
"github.com/moby/moby/api/types/filters" |
| 16 |
+ "github.com/moby/moby/api/types/network" |
|
| 16 | 17 |
"github.com/moby/moby/v2/daemon/container" |
| 17 | 18 |
"github.com/moby/moby/v2/daemon/internal/image" |
| 18 | 19 |
"github.com/moby/moby/v2/daemon/server/backend" |
| ... | ... |
@@ -403,7 +404,7 @@ func portOp(key string, filter map[string]bool) func(value string) error {
|
| 403 | 403 |
return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value)
|
| 404 | 404 |
} |
| 405 | 405 |
// support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] |
| 406 |
- portRange, err := containertypes.ParsePortRange(value) |
|
| 406 |
+ portRange, err := network.ParsePortRange(value) |
|
| 407 | 407 |
if err != nil {
|
| 408 | 408 |
return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
|
| 409 | 409 |
} |
| ... | ... |
@@ -1027,13 +1027,13 @@ func buildPortsRelatedCreateEndpointOptions(c *container.Container, n *libnetwor |
| 1027 | 1027 |
|
| 1028 | 1028 |
for _, binding := range bindings {
|
| 1029 | 1029 |
var ( |
| 1030 |
- portRange containertypes.PortRange |
|
| 1030 |
+ portRange networktypes.PortRange |
|
| 1031 | 1031 |
err error |
| 1032 | 1032 |
) |
| 1033 | 1033 |
|
| 1034 | 1034 |
// Empty HostPort means to map to an ephemeral port. |
| 1035 | 1035 |
if binding.HostPort != "" {
|
| 1036 |
- portRange, err = containertypes.ParsePortRange(binding.HostPort) |
|
| 1036 |
+ portRange, err = networktypes.ParsePortRange(binding.HostPort) |
|
| 1037 | 1037 |
if err != nil {
|
| 1038 | 1038 |
return nil, fmt.Errorf("error parsing HostPort value(%s):%v", binding.HostPort, err)
|
| 1039 | 1039 |
} |
| ... | ... |
@@ -1063,8 +1063,8 @@ func buildPortsRelatedCreateEndpointOptions(c *container.Container, n *libnetwor |
| 1063 | 1063 |
} |
| 1064 | 1064 |
|
| 1065 | 1065 |
// getPortMapInfo retrieves the current port-mapping programmed for the given sandbox |
| 1066 |
-func getPortMapInfo(sb *libnetwork.Sandbox) containertypes.PortMap {
|
|
| 1067 |
- pm := containertypes.PortMap{}
|
|
| 1066 |
+func getPortMapInfo(sb *libnetwork.Sandbox) networktypes.PortMap {
|
|
| 1067 |
+ pm := networktypes.PortMap{}
|
|
| 1068 | 1068 |
if sb == nil {
|
| 1069 | 1069 |
return pm |
| 1070 | 1070 |
} |
| ... | ... |
@@ -1075,7 +1075,7 @@ func getPortMapInfo(sb *libnetwork.Sandbox) containertypes.PortMap {
|
| 1075 | 1075 |
return pm |
| 1076 | 1076 |
} |
| 1077 | 1077 |
|
| 1078 |
-func getEndpointPortMapInfo(pm containertypes.PortMap, ep *libnetwork.Endpoint) {
|
|
| 1078 |
+func getEndpointPortMapInfo(pm networktypes.PortMap, ep *libnetwork.Endpoint) {
|
|
| 1079 | 1079 |
driverInfo, _ := ep.DriverInfo() |
| 1080 | 1080 |
if driverInfo == nil {
|
| 1081 | 1081 |
// It is not an error for epInfo to be nil |
| ... | ... |
@@ -1085,7 +1085,7 @@ func getEndpointPortMapInfo(pm containertypes.PortMap, ep *libnetwork.Endpoint) |
| 1085 | 1085 |
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
|
| 1086 | 1086 |
if exposedPorts, ok := expData.([]lntypes.TransportPort); ok {
|
| 1087 | 1087 |
for _, tp := range exposedPorts {
|
| 1088 |
- natPort, ok := containertypes.PortFrom(tp.Port, containertypes.NetworkProtocol(tp.Proto.String())) |
|
| 1088 |
+ natPort, ok := networktypes.PortFrom(tp.Port, networktypes.IPProtocol(tp.Proto.String())) |
|
| 1089 | 1089 |
if !ok {
|
| 1090 | 1090 |
log.G(context.TODO()).Errorf("Invalid exposed port: %s", tp.String())
|
| 1091 | 1091 |
continue |
| ... | ... |
@@ -1105,7 +1105,7 @@ func getEndpointPortMapInfo(pm containertypes.PortMap, ep *libnetwork.Endpoint) |
| 1105 | 1105 |
if portMapping, ok := mapData.([]lntypes.PortBinding); ok {
|
| 1106 | 1106 |
for _, pp := range portMapping {
|
| 1107 | 1107 |
// Use an empty string for the host natPort if there's no natPort assigned. |
| 1108 |
- natPort, ok := containertypes.PortFrom(pp.Port, containertypes.NetworkProtocol(pp.Proto.String())) |
|
| 1108 |
+ natPort, ok := networktypes.PortFrom(pp.Port, networktypes.IPProtocol(pp.Proto.String())) |
|
| 1109 | 1109 |
if !ok {
|
| 1110 | 1110 |
log.G(context.TODO()).Errorf("Invalid port binding: %s", pp.String())
|
| 1111 | 1111 |
continue |
| ... | ... |
@@ -1115,7 +1115,7 @@ func getEndpointPortMapInfo(pm containertypes.PortMap, ep *libnetwork.Endpoint) |
| 1115 | 1115 |
if pp.HostPort > 0 {
|
| 1116 | 1116 |
hp = strconv.Itoa(int(pp.HostPort)) |
| 1117 | 1117 |
} |
| 1118 |
- natBndg := containertypes.PortBinding{HostPort: hp}
|
|
| 1118 |
+ natBndg := networktypes.PortBinding{HostPort: hp}
|
|
| 1119 | 1119 |
natBndg.HostIP, _ = netip.AddrFromSlice(pp.HostIP) |
| 1120 | 1120 |
pm[natPort] = append(pm[natPort], natBndg) |
| 1121 | 1121 |
} |
| ... | ... |
@@ -4,7 +4,6 @@ import ( |
| 4 | 4 |
"net" |
| 5 | 5 |
"sync" |
| 6 | 6 |
|
| 7 |
- "github.com/moby/moby/api/types/container" |
|
| 8 | 7 |
networktypes "github.com/moby/moby/api/types/network" |
| 9 | 8 |
clustertypes "github.com/moby/moby/v2/daemon/cluster/provider" |
| 10 | 9 |
"github.com/pkg/errors" |
| ... | ... |
@@ -17,7 +16,7 @@ type Settings struct {
|
| 17 | 17 |
SandboxKey string |
| 18 | 18 |
Networks map[string]*EndpointSettings |
| 19 | 19 |
Service *clustertypes.ServiceConfig |
| 20 |
- Ports container.PortMap |
|
| 20 |
+ Ports networktypes.PortMap |
|
| 21 | 21 |
HasSwarmEndpoint bool |
| 22 | 22 |
} |
| 23 | 23 |
|
| ... | ... |
@@ -901,7 +901,7 @@ func handlePortBindingsBC(hostConfig *container.HostConfig, version string) stri |
| 901 | 901 |
emptyPBs = append(emptyPBs, port.String()) |
| 902 | 902 |
} |
| 903 | 903 |
|
| 904 |
- hostConfig.PortBindings[port] = []container.PortBinding{{}}
|
|
| 904 |
+ hostConfig.PortBindings[port] = []network.PortBinding{{}}
|
|
| 905 | 905 |
} |
| 906 | 906 |
|
| 907 | 907 |
if len(emptyPBs) > 0 {
|
| ... | ... |
@@ -504,8 +504,8 @@ func (s *DockerAPISuite) TestContainerAPIBadPort(c *testing.T) {
|
| 504 | 504 |
} |
| 505 | 505 |
|
| 506 | 506 |
hostConfig := container.HostConfig{
|
| 507 |
- PortBindings: container.PortMap{
|
|
| 508 |
- container.MustParsePort("8080/tcp"): []container.PortBinding{
|
|
| 507 |
+ PortBindings: network.PortMap{
|
|
| 508 |
+ network.MustParsePort("8080/tcp"): []network.PortBinding{
|
|
| 509 | 509 |
{
|
| 510 | 510 |
HostPort: "aa80", |
| 511 | 511 |
}, |
| ... | ... |
@@ -9,7 +9,7 @@ import ( |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| 11 | 11 |
|
| 12 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 12 |
+ "github.com/moby/moby/api/types/network" |
|
| 13 | 13 |
"github.com/moby/moby/v2/integration-cli/cli" |
| 14 | 14 |
"github.com/moby/moby/v2/integration-cli/cli/build" |
| 15 | 15 |
"github.com/moby/moby/v2/internal/testutil/fakecontext" |
| ... | ... |
@@ -92,7 +92,7 @@ func (s *DockerCLICreateSuite) TestCreateWithPortRange(c *testing.T) {
|
| 92 | 92 |
|
| 93 | 93 |
var containers []struct {
|
| 94 | 94 |
HostConfig *struct {
|
| 95 |
- PortBindings map[containertypes.Port][]containertypes.PortBinding |
|
| 95 |
+ PortBindings network.PortMap |
|
| 96 | 96 |
} |
| 97 | 97 |
} |
| 98 | 98 |
err := json.Unmarshal([]byte(out), &containers) |
| ... | ... |
@@ -118,7 +118,7 @@ func (s *DockerCLICreateSuite) TestCreateWithLargePortRange(c *testing.T) {
|
| 118 | 118 |
|
| 119 | 119 |
var containers []struct {
|
| 120 | 120 |
HostConfig *struct {
|
| 121 |
- PortBindings map[containertypes.Port][]containertypes.PortBinding |
|
| 121 |
+ PortBindings network.PortMap |
|
| 122 | 122 |
} |
| 123 | 123 |
} |
| 124 | 124 |
|
| ... | ... |
@@ -22,7 +22,7 @@ import ( |
| 22 | 22 |
"testing" |
| 23 | 23 |
"time" |
| 24 | 24 |
|
| 25 |
- "github.com/moby/moby/api/types/container" |
|
| 25 |
+ "github.com/moby/moby/api/types/network" |
|
| 26 | 26 |
"github.com/moby/moby/client" |
| 27 | 27 |
"github.com/moby/moby/client/pkg/stringid" |
| 28 | 28 |
"github.com/moby/moby/v2/integration-cli/cli" |
| ... | ... |
@@ -2168,7 +2168,7 @@ func (s *DockerCLIRunSuite) TestRunAllowPortRangeThroughExpose(c *testing.T) {
|
| 2168 | 2168 |
id = strings.TrimSpace(id) |
| 2169 | 2169 |
|
| 2170 | 2170 |
portstr := inspectFieldJSON(c, id, "NetworkSettings.Ports") |
| 2171 |
- var ports container.PortMap |
|
| 2171 |
+ var ports network.PortMap |
|
| 2172 | 2172 |
if err := json.Unmarshal([]byte(portstr), &ports); err != nil {
|
| 2173 | 2173 |
c.Fatal(err) |
| 2174 | 2174 |
} |
| ... | ... |
@@ -2505,7 +2505,7 @@ func (s *DockerCLIRunSuite) TestRunAllowPortRangeThroughPublish(c *testing.T) {
|
| 2505 | 2505 |
id = strings.TrimSpace(id) |
| 2506 | 2506 |
portStr := inspectFieldJSON(c, id, "NetworkSettings.Ports") |
| 2507 | 2507 |
|
| 2508 |
- var ports container.PortMap |
|
| 2508 |
+ var ports network.PortMap |
|
| 2509 | 2509 |
err := json.Unmarshal([]byte(portStr), &ports) |
| 2510 | 2510 |
assert.NilError(c, err, "failed to unmarshal: %v", portStr) |
| 2511 | 2511 |
for port, binding := range ports {
|
| ... | ... |
@@ -3,7 +3,7 @@ package container |
| 3 | 3 |
import ( |
| 4 | 4 |
"testing" |
| 5 | 5 |
|
| 6 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 6 |
+ "github.com/moby/moby/api/types/network" |
|
| 7 | 7 |
"github.com/moby/moby/client" |
| 8 | 8 |
"github.com/moby/moby/v2/integration/internal/container" |
| 9 | 9 |
"github.com/moby/moby/v2/internal/testutil" |
| ... | ... |
@@ -71,13 +71,13 @@ func TestNetworkStateCleanupOnDaemonStart(t *testing.T) {
|
| 71 | 71 |
defer d.Stop(t) |
| 72 | 72 |
|
| 73 | 73 |
apiClient := d.NewClientT(t) |
| 74 |
- mappedPort := containertypes.MustParsePort("80/tcp")
|
|
| 74 |
+ mappedPort := network.MustParsePort("80/tcp")
|
|
| 75 | 75 |
|
| 76 | 76 |
// The intention of this container is to ignore stop signals. |
| 77 | 77 |
// Sadly this means the test will take longer, but at least this test can be parallelized. |
| 78 | 78 |
cid := container.Run(ctx, t, apiClient, |
| 79 | 79 |
container.WithExposedPorts("80/tcp"),
|
| 80 |
- container.WithPortMap(containertypes.PortMap{mappedPort: {{}}}),
|
|
| 80 |
+ container.WithPortMap(network.PortMap{mappedPort: {{}}}),
|
|
| 81 | 81 |
container.WithCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done"))
|
| 82 | 82 |
defer func() {
|
| 83 | 83 |
err := apiClient.ContainerRemove(ctx, cid, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -10,7 +10,7 @@ import ( |
| 10 | 10 |
"strings" |
| 11 | 11 |
"testing" |
| 12 | 12 |
|
| 13 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 13 |
+ "github.com/moby/moby/api/types/network" |
|
| 14 | 14 |
"github.com/moby/moby/client" |
| 15 | 15 |
"github.com/moby/moby/v2/integration/internal/container" |
| 16 | 16 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -122,8 +122,8 @@ func startServerContainer(ctx context.Context, t *testing.T, msg string, port ui |
| 122 | 122 |
container.WithCmd("sh", "-c", fmt.Sprintf("echo %q | nc -lp %d", msg, port)),
|
| 123 | 123 |
container.WithExposedPorts(fmt.Sprintf("%d/tcp", port)),
|
| 124 | 124 |
func(c *container.TestContainerConfig) {
|
| 125 |
- c.HostConfig.PortBindings = containertypes.PortMap{
|
|
| 126 |
- containertypes.MustParsePort(fmt.Sprintf("%d/tcp", port)): []containertypes.PortBinding{
|
|
| 125 |
+ c.HostConfig.PortBindings = network.PortMap{
|
|
| 126 |
+ network.MustParsePort(fmt.Sprintf("%d/tcp", port)): []network.PortBinding{
|
|
| 127 | 127 |
{
|
| 128 | 128 |
HostPort: fmt.Sprintf("%d", port),
|
| 129 | 129 |
}, |
| ... | ... |
@@ -74,18 +74,18 @@ func WithSysctls(sysctls map[string]string) func(*TestContainerConfig) {
|
| 74 | 74 |
// WithExposedPorts sets the exposed ports of the container |
| 75 | 75 |
func WithExposedPorts(ports ...string) func(*TestContainerConfig) {
|
| 76 | 76 |
return func(c *TestContainerConfig) {
|
| 77 |
- c.Config.ExposedPorts = map[container.Port]struct{}{}
|
|
| 77 |
+ c.Config.ExposedPorts = map[network.Port]struct{}{}
|
|
| 78 | 78 |
for _, port := range ports {
|
| 79 |
- p, _ := container.ParsePort(port) |
|
| 79 |
+ p, _ := network.ParsePort(port) |
|
| 80 | 80 |
c.Config.ExposedPorts[p] = struct{}{}
|
| 81 | 81 |
} |
| 82 | 82 |
} |
| 83 | 83 |
} |
| 84 | 84 |
|
| 85 | 85 |
// WithPortMap sets/replaces port mappings. |
| 86 |
-func WithPortMap(pm container.PortMap) func(*TestContainerConfig) {
|
|
| 86 |
+func WithPortMap(pm network.PortMap) func(*TestContainerConfig) {
|
|
| 87 | 87 |
return func(c *TestContainerConfig) {
|
| 88 |
- c.HostConfig.PortBindings = container.PortMap{}
|
|
| 88 |
+ c.HostConfig.PortBindings = network.PortMap{}
|
|
| 89 | 89 |
for p, b := range pm {
|
| 90 | 90 |
c.HostConfig.PortBindings[p] = slices.Clone(b) |
| 91 | 91 |
} |
| ... | ... |
@@ -516,19 +516,19 @@ func TestEndpointWithCustomIfname(t *testing.T) {
|
| 516 | 516 |
func TestPublishedPortAlreadyInUse(t *testing.T) {
|
| 517 | 517 |
ctx := setupTest(t) |
| 518 | 518 |
apiClient := testEnv.APIClient() |
| 519 |
- mappedPort := containertypes.MustParsePort("80/tcp")
|
|
| 519 |
+ mappedPort := networktypes.MustParsePort("80/tcp")
|
|
| 520 | 520 |
|
| 521 | 521 |
ctr1 := ctr.Run(ctx, t, apiClient, |
| 522 | 522 |
ctr.WithCmd("top"),
|
| 523 | 523 |
ctr.WithExposedPorts("80/tcp"),
|
| 524 |
- ctr.WithPortMap(containertypes.PortMap{mappedPort: {{HostPort: "8000"}}}))
|
|
| 524 |
+ ctr.WithPortMap(networktypes.PortMap{mappedPort: {{HostPort: "8000"}}}))
|
|
| 525 | 525 |
defer ctr.Remove(ctx, t, apiClient, ctr1, client.ContainerRemoveOptions{Force: true})
|
| 526 | 526 |
|
| 527 | 527 |
ctr2 := ctr.Create(ctx, t, apiClient, |
| 528 | 528 |
ctr.WithCmd("top"),
|
| 529 | 529 |
ctr.WithRestartPolicy(containertypes.RestartPolicyAlways), |
| 530 | 530 |
ctr.WithExposedPorts("80/tcp"),
|
| 531 |
- ctr.WithPortMap(containertypes.PortMap{mappedPort: {{HostPort: "8000"}}}))
|
|
| 531 |
+ ctr.WithPortMap(networktypes.PortMap{mappedPort: {{HostPort: "8000"}}}))
|
|
| 532 | 532 |
defer ctr.Remove(ctx, t, apiClient, ctr2, client.ContainerRemoveOptions{Force: true})
|
| 533 | 533 |
|
| 534 | 534 |
err := apiClient.ContainerStart(ctx, ctr2, client.ContainerStartOptions{})
|
| ... | ... |
@@ -563,18 +563,18 @@ func TestAllPortMappingsAreReturned(t *testing.T) {
|
| 563 | 563 |
|
| 564 | 564 |
ctrID := ctr.Run(ctx, t, apiClient, |
| 565 | 565 |
ctr.WithExposedPorts("80/tcp", "81/tcp"),
|
| 566 |
- ctr.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8000"}}}),
|
|
| 566 |
+ ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8000"}}}),
|
|
| 567 | 567 |
ctr.WithEndpointSettings("testnetv4", &networktypes.EndpointSettings{}),
|
| 568 | 568 |
ctr.WithEndpointSettings("testnetv6", &networktypes.EndpointSettings{}))
|
| 569 | 569 |
defer ctr.Remove(ctx, t, apiClient, ctrID, client.ContainerRemoveOptions{Force: true})
|
| 570 | 570 |
|
| 571 | 571 |
inspect := ctr.Inspect(ctx, t, apiClient, ctrID) |
| 572 |
- assert.DeepEqual(t, inspect.NetworkSettings.Ports, containertypes.PortMap{
|
|
| 573 |
- containertypes.MustParsePort("80/tcp"): []containertypes.PortBinding{
|
|
| 572 |
+ assert.DeepEqual(t, inspect.NetworkSettings.Ports, networktypes.PortMap{
|
|
| 573 |
+ networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
|
|
| 574 | 574 |
{HostIP: netip.IPv4Unspecified(), HostPort: "8000"},
|
| 575 | 575 |
{HostIP: netip.IPv6Unspecified(), HostPort: "8000"},
|
| 576 | 576 |
}, |
| 577 |
- containertypes.MustParsePort("81/tcp"): nil,
|
|
| 577 |
+ networktypes.MustParsePort("81/tcp"): nil,
|
|
| 578 | 578 |
}, cmpopts.EquateComparable(netip.Addr{}))
|
| 579 | 579 |
} |
| 580 | 580 |
|
| ... | ... |
@@ -602,7 +602,7 @@ func TestFirewalldReloadNoZombies(t *testing.T) {
|
| 602 | 602 |
|
| 603 | 603 |
cid := ctr.Run(ctx, t, c, |
| 604 | 604 |
ctr.WithExposedPorts("80/tcp", "81/tcp"),
|
| 605 |
- ctr.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8000"}}}))
|
|
| 605 |
+ ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8000"}}}))
|
|
| 606 | 606 |
defer func() {
|
| 607 | 607 |
if !removed {
|
| 608 | 608 |
ctr.Remove(ctx, t, c, cid, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -792,7 +792,7 @@ func TestPortMappingRestore(t *testing.T) {
|
| 792 | 792 |
const svrName = "svr" |
| 793 | 793 |
cid := ctr.Run(ctx, t, c, |
| 794 | 794 |
ctr.WithExposedPorts("80/tcp"),
|
| 795 |
- ctr.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {}}),
|
|
| 795 |
+ ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {}}),
|
|
| 796 | 796 |
ctr.WithName(svrName), |
| 797 | 797 |
ctr.WithRestartPolicy(containertypes.RestartPolicyUnlessStopped), |
| 798 | 798 |
ctr.WithCmd("httpd", "-f"),
|
| ... | ... |
@@ -803,9 +803,9 @@ func TestPortMappingRestore(t *testing.T) {
|
| 803 | 803 |
t.Helper() |
| 804 | 804 |
insp := ctr.Inspect(ctx, t, c, cid) |
| 805 | 805 |
assert.Check(t, is.Equal(insp.State.Running, true)) |
| 806 |
- if assert.Check(t, is.Contains(insp.NetworkSettings.Ports, containertypes.MustParsePort("80/tcp"))) &&
|
|
| 807 |
- assert.Check(t, is.Len(insp.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")], 2)) {
|
|
| 808 |
- hostPort := insp.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 806 |
+ if assert.Check(t, is.Contains(insp.NetworkSettings.Ports, networktypes.MustParsePort("80/tcp"))) &&
|
|
| 807 |
+ assert.Check(t, is.Len(insp.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")], 2)) {
|
|
| 808 |
+ hostPort := insp.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 809 | 809 |
res := ctr.RunAttach(ctx, t, c, |
| 810 | 810 |
ctr.WithExtraHost("thehost:host-gateway"),
|
| 811 | 811 |
ctr.WithCmd("wget", "-T3", "http://"+net.JoinHostPort("thehost", hostPort)),
|
| ... | ... |
@@ -950,7 +950,7 @@ func TestEmptyPortBindingsBC(t *testing.T) {
|
| 950 | 950 |
d.StartWithBusybox(ctx, t) |
| 951 | 951 |
defer d.Stop(t) |
| 952 | 952 |
|
| 953 |
- createInspect := func(t *testing.T, version string, pbs []containertypes.PortBinding) (containertypes.PortMap, []string) {
|
|
| 953 |
+ createInspect := func(t *testing.T, version string, pbs []networktypes.PortBinding) (networktypes.PortMap, []string) {
|
|
| 954 | 954 |
apiClient := d.NewClientT(t, client.WithVersion(version)) |
| 955 | 955 |
defer apiClient.Close() |
| 956 | 956 |
|
| ... | ... |
@@ -965,7 +965,7 @@ func TestEmptyPortBindingsBC(t *testing.T) {
|
| 965 | 965 |
// Create a container with an empty list of port bindings for container port 80/tcp. |
| 966 | 966 |
config := ctr.NewTestConfig(ctr.WithCmd("top"),
|
| 967 | 967 |
ctr.WithExposedPorts("80/tcp"),
|
| 968 |
- ctr.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): pbs}))
|
|
| 968 |
+ ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): pbs}))
|
|
| 969 | 969 |
c, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) |
| 970 | 970 |
assert.NilError(t, err) |
| 971 | 971 |
defer apiClient.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -978,46 +978,46 @@ func TestEmptyPortBindingsBC(t *testing.T) {
|
| 978 | 978 |
} |
| 979 | 979 |
|
| 980 | 980 |
t.Run("backfilling on old client version", func(t *testing.T) {
|
| 981 |
- expMappings := containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {
|
|
| 981 |
+ expMappings := networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {
|
|
| 982 | 982 |
{}, // An empty PortBinding is backfilled
|
| 983 | 983 |
}} |
| 984 | 984 |
expWarnings := make([]string, 0) |
| 985 | 985 |
|
| 986 |
- mappings, warnings := createInspect(t, "1.51", []containertypes.PortBinding{})
|
|
| 986 |
+ mappings, warnings := createInspect(t, "1.51", []networktypes.PortBinding{})
|
|
| 987 | 987 |
assert.DeepEqual(t, expMappings, mappings, cmpopts.EquateComparable(netip.Addr{}))
|
| 988 | 988 |
assert.DeepEqual(t, expWarnings, warnings, cmpopts.EquateComparable(netip.Addr{}))
|
| 989 | 989 |
}) |
| 990 | 990 |
|
| 991 | 991 |
t.Run("backfilling on API 1.52, with a warning", func(t *testing.T) {
|
| 992 |
- expMappings := containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {
|
|
| 992 |
+ expMappings := networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {
|
|
| 993 | 993 |
{}, // An empty PortBinding is backfilled
|
| 994 | 994 |
}} |
| 995 | 995 |
expWarnings := []string{
|
| 996 | 996 |
"Following container port(s) have an empty list of port-bindings: 80/tcp. Starting with API 1.53, such bindings will be discarded.", |
| 997 | 997 |
} |
| 998 | 998 |
|
| 999 |
- mappings, warnings := createInspect(t, "1.52", []containertypes.PortBinding{})
|
|
| 999 |
+ mappings, warnings := createInspect(t, "1.52", []networktypes.PortBinding{})
|
|
| 1000 | 1000 |
assert.DeepEqual(t, expMappings, mappings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1001 | 1001 |
assert.DeepEqual(t, expWarnings, warnings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1002 | 1002 |
}) |
| 1003 | 1003 |
|
| 1004 | 1004 |
t.Run("no backfilling on API 1.53", func(t *testing.T) {
|
| 1005 |
- expMappings := containertypes.PortMap{}
|
|
| 1005 |
+ expMappings := networktypes.PortMap{}
|
|
| 1006 | 1006 |
expWarnings := make([]string, 0) |
| 1007 | 1007 |
|
| 1008 |
- mappings, warnings := createInspect(t, "1.53", []containertypes.PortBinding{})
|
|
| 1008 |
+ mappings, warnings := createInspect(t, "1.53", []networktypes.PortBinding{})
|
|
| 1009 | 1009 |
assert.DeepEqual(t, expMappings, mappings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1010 | 1010 |
assert.DeepEqual(t, expWarnings, warnings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1011 | 1011 |
}) |
| 1012 | 1012 |
|
| 1013 | 1013 |
for _, apiVersion := range []string{"1.51", "1.52", "1.53"} {
|
| 1014 | 1014 |
t.Run("no backfilling on API "+apiVersion+" with non-empty bindings", func(t *testing.T) {
|
| 1015 |
- expMappings := containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {
|
|
| 1015 |
+ expMappings := networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {
|
|
| 1016 | 1016 |
{HostPort: "8080"},
|
| 1017 | 1017 |
}} |
| 1018 | 1018 |
expWarnings := make([]string, 0) |
| 1019 | 1019 |
|
| 1020 |
- mappings, warnings := createInspect(t, apiVersion, []containertypes.PortBinding{{HostPort: "8080"}})
|
|
| 1020 |
+ mappings, warnings := createInspect(t, apiVersion, []networktypes.PortBinding{{HostPort: "8080"}})
|
|
| 1021 | 1021 |
assert.DeepEqual(t, expMappings, mappings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1022 | 1022 |
assert.DeepEqual(t, expWarnings, warnings, cmpopts.EquateComparable(netip.Addr{}))
|
| 1023 | 1023 |
}) |
| ... | ... |
@@ -1042,7 +1042,7 @@ func TestPortBindingBackfillingForOlderContainers(t *testing.T) {
|
| 1042 | 1042 |
|
| 1043 | 1043 |
cid := ctr.Create(ctx, t, c, |
| 1044 | 1044 |
ctr.WithExposedPorts("80/tcp"),
|
| 1045 |
- ctr.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {}}))
|
|
| 1045 |
+ ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {}}))
|
|
| 1046 | 1046 |
defer c.ContainerRemove(ctx, cid, client.ContainerRemoveOptions{Force: true})
|
| 1047 | 1047 |
|
| 1048 | 1048 |
// Stop the daemon to safely tamper with the on-disk state. |
| ... | ... |
@@ -1051,7 +1051,7 @@ func TestPortBindingBackfillingForOlderContainers(t *testing.T) {
|
| 1051 | 1051 |
d.TamperWithContainerConfig(t, cid, func(container *container.Container) {
|
| 1052 | 1052 |
// Simulate a container created with an older version of the Engine |
| 1053 | 1053 |
// by setting an empty list of port bindings. |
| 1054 |
- container.HostConfig.PortBindings = containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {}}
|
|
| 1054 |
+ container.HostConfig.PortBindings = networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {}}
|
|
| 1055 | 1055 |
}) |
| 1056 | 1056 |
|
| 1057 | 1057 |
// Restart the daemon — it should backfill the empty port binding slice. |
| ... | ... |
@@ -1059,7 +1059,7 @@ func TestPortBindingBackfillingForOlderContainers(t *testing.T) {
|
| 1059 | 1059 |
|
| 1060 | 1060 |
inspect := ctr.Inspect(ctx, t, c, cid) |
| 1061 | 1061 |
|
| 1062 |
- expMappings := containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {
|
|
| 1062 |
+ expMappings := networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {
|
|
| 1063 | 1063 |
{}, // An empty PortBinding is backfilled
|
| 1064 | 1064 |
}} |
| 1065 | 1065 |
assert.DeepEqual(t, expMappings, inspect.HostConfig.PortBindings, cmpopts.EquateComparable(netip.Addr{}))
|
| ... | ... |
@@ -30,7 +30,7 @@ import ( |
| 30 | 30 |
"text/template" |
| 31 | 31 |
"time" |
| 32 | 32 |
|
| 33 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 33 |
+ networktypes "github.com/moby/moby/api/types/network" |
|
| 34 | 34 |
swarmtypes "github.com/moby/moby/api/types/swarm" |
| 35 | 35 |
"github.com/moby/moby/client" |
| 36 | 36 |
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge" |
| ... | ... |
@@ -53,7 +53,7 @@ var ( |
| 53 | 53 |
|
| 54 | 54 |
type ctrDesc struct {
|
| 55 | 55 |
name string |
| 56 |
- portMappings containertypes.PortMap |
|
| 56 |
+ portMappings networktypes.PortMap |
|
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 | 59 |
type networkDesc struct {
|
| ... | ... |
@@ -82,7 +82,7 @@ var index = []section{
|
| 82 | 82 |
containers: []ctrDesc{
|
| 83 | 83 |
{
|
| 84 | 84 |
name: "c1", |
| 85 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 85 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 86 | 86 |
}, |
| 87 | 87 |
}, |
| 88 | 88 |
}}, |
| ... | ... |
@@ -95,7 +95,7 @@ var index = []section{
|
| 95 | 95 |
containers: []ctrDesc{
|
| 96 | 96 |
{
|
| 97 | 97 |
name: "c1", |
| 98 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 98 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 99 | 99 |
}, |
| 100 | 100 |
}, |
| 101 | 101 |
}}, |
| ... | ... |
@@ -108,7 +108,7 @@ var index = []section{
|
| 108 | 108 |
containers: []ctrDesc{
|
| 109 | 109 |
{
|
| 110 | 110 |
name: "c1", |
| 111 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 111 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 112 | 112 |
}, |
| 113 | 113 |
}, |
| 114 | 114 |
}}, |
| ... | ... |
@@ -142,7 +142,7 @@ var index = []section{
|
| 142 | 142 |
containers: []ctrDesc{
|
| 143 | 143 |
{
|
| 144 | 144 |
name: "c1", |
| 145 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 145 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 146 | 146 |
}, |
| 147 | 147 |
}, |
| 148 | 148 |
}}, |
| ... | ... |
@@ -155,7 +155,7 @@ var index = []section{
|
| 155 | 155 |
containers: []ctrDesc{
|
| 156 | 156 |
{
|
| 157 | 157 |
name: "c1", |
| 158 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 158 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 159 | 159 |
}, |
| 160 | 160 |
}, |
| 161 | 161 |
}}, |
| ... | ... |
@@ -167,7 +167,7 @@ var index = []section{
|
| 167 | 167 |
containers: []ctrDesc{
|
| 168 | 168 |
{
|
| 169 | 169 |
name: "c1", |
| 170 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 170 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 171 | 171 |
}, |
| 172 | 172 |
}, |
| 173 | 173 |
}}, |
| ... | ... |
@@ -179,7 +179,7 @@ var index = []section{
|
| 179 | 179 |
containers: []ctrDesc{
|
| 180 | 180 |
{
|
| 181 | 181 |
name: "c1", |
| 182 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("127.0.0.1"), HostPort: "8080"}}},
|
|
| 182 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("127.0.0.1"), HostPort: "8080"}}},
|
|
| 183 | 183 |
}, |
| 184 | 184 |
}, |
| 185 | 185 |
}}, |
| ... | ... |
@@ -28,7 +28,7 @@ import ( |
| 28 | 28 |
"testing" |
| 29 | 29 |
"text/template" |
| 30 | 30 |
|
| 31 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 31 |
+ networktypes "github.com/moby/moby/api/types/network" |
|
| 32 | 32 |
swarmtypes "github.com/moby/moby/api/types/swarm" |
| 33 | 33 |
"github.com/moby/moby/client" |
| 34 | 34 |
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge" |
| ... | ... |
@@ -50,7 +50,7 @@ var ( |
| 50 | 50 |
|
| 51 | 51 |
type ctrDesc struct {
|
| 52 | 52 |
name string |
| 53 |
- portMappings containertypes.PortMap |
|
| 53 |
+ portMappings networktypes.PortMap |
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 | 56 |
type networkDesc struct {
|
| ... | ... |
@@ -79,7 +79,7 @@ var index = []section{
|
| 79 | 79 |
containers: []ctrDesc{
|
| 80 | 80 |
{
|
| 81 | 81 |
name: "c1", |
| 82 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 82 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 83 | 83 |
}, |
| 84 | 84 |
}, |
| 85 | 85 |
}}, |
| ... | ... |
@@ -92,7 +92,7 @@ var index = []section{
|
| 92 | 92 |
containers: []ctrDesc{
|
| 93 | 93 |
{
|
| 94 | 94 |
name: "c1", |
| 95 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 95 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 96 | 96 |
}, |
| 97 | 97 |
}, |
| 98 | 98 |
}}, |
| ... | ... |
@@ -105,7 +105,7 @@ var index = []section{
|
| 105 | 105 |
containers: []ctrDesc{
|
| 106 | 106 |
{
|
| 107 | 107 |
name: "c1", |
| 108 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 108 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 109 | 109 |
}, |
| 110 | 110 |
}, |
| 111 | 111 |
}}, |
| ... | ... |
@@ -139,7 +139,7 @@ var index = []section{
|
| 139 | 139 |
containers: []ctrDesc{
|
| 140 | 140 |
{
|
| 141 | 141 |
name: "c1", |
| 142 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 142 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 143 | 143 |
}, |
| 144 | 144 |
}, |
| 145 | 145 |
}}, |
| ... | ... |
@@ -152,7 +152,7 @@ var index = []section{
|
| 152 | 152 |
containers: []ctrDesc{
|
| 153 | 153 |
{
|
| 154 | 154 |
name: "c1", |
| 155 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 155 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}},
|
|
| 156 | 156 |
}, |
| 157 | 157 |
}, |
| 158 | 158 |
}}, |
| ... | ... |
@@ -178,7 +178,7 @@ var index = []section{
|
| 178 | 178 |
containers: []ctrDesc{
|
| 179 | 179 |
{
|
| 180 | 180 |
name: "c1", |
| 181 |
- portMappings: containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("127.0.0.1"), HostPort: "8080"}}},
|
|
| 181 |
+ portMappings: networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("127.0.0.1"), HostPort: "8080"}}},
|
|
| 182 | 182 |
}, |
| 183 | 183 |
}, |
| 184 | 184 |
}}, |
| ... | ... |
@@ -17,7 +17,6 @@ import ( |
| 17 | 17 |
"time" |
| 18 | 18 |
|
| 19 | 19 |
"github.com/google/go-cmp/cmp/cmpopts" |
| 20 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 21 | 20 |
networktypes "github.com/moby/moby/api/types/network" |
| 22 | 21 |
"github.com/moby/moby/client" |
| 23 | 22 |
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge" |
| ... | ... |
@@ -388,7 +387,7 @@ func TestBridgeINCRouted(t *testing.T) {
|
| 388 | 388 |
container.WithNetworkMode(netName), |
| 389 | 389 |
container.WithName("ctr-"+gwMode),
|
| 390 | 390 |
container.WithExposedPorts("80/tcp"),
|
| 391 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {}}),
|
|
| 391 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {}}),
|
|
| 392 | 392 |
) |
| 393 | 393 |
t.Cleanup(func() {
|
| 394 | 394 |
c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -565,7 +564,7 @@ func TestAccessToPublishedPort(t *testing.T) {
|
| 565 | 565 |
container.WithNetworkMode(serverNetName), |
| 566 | 566 |
container.WithName("ctr-server"),
|
| 567 | 567 |
container.WithExposedPorts("80/tcp"),
|
| 568 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {containertypes.PortBinding{HostPort: "8080"}}}),
|
|
| 568 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {networktypes.PortBinding{HostPort: "8080"}}}),
|
|
| 569 | 569 |
container.WithCmd("httpd", "-f"),
|
| 570 | 570 |
) |
| 571 | 571 |
defer c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -688,7 +687,7 @@ func TestInterNetworkDirectRouting(t *testing.T) {
|
| 688 | 688 |
container.WithNetworkMode(serverNetName), |
| 689 | 689 |
container.WithName("ctr-pub"),
|
| 690 | 690 |
container.WithExposedPorts("80/tcp"),
|
| 691 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {containertypes.PortBinding{HostPort: "8080"}}}),
|
|
| 691 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {networktypes.PortBinding{HostPort: "8080"}}}),
|
|
| 692 | 692 |
container.WithCmd("httpd", "-f"),
|
| 693 | 693 |
) |
| 694 | 694 |
defer c.ContainerRemove(ctx, ctrPubId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -1063,7 +1062,7 @@ func TestDisableIPv6OnInterface(t *testing.T) {
|
| 1063 | 1063 |
container.WithName(ctrName), |
| 1064 | 1064 |
container.WithNetworkMode(tc.netName), |
| 1065 | 1065 |
container.WithExposedPorts("80/tcp"),
|
| 1066 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}}),
|
|
| 1066 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}}),
|
|
| 1067 | 1067 |
container.WithEndpointSettings(tc.netName, &networktypes.EndpointSettings{
|
| 1068 | 1068 |
DriverOpts: map[string]string{
|
| 1069 | 1069 |
netlabel.EndpointSysctls: "net.ipv6.conf.IFNAME.disable_ipv6=1", |
| ... | ... |
@@ -1505,7 +1504,7 @@ func TestGatewaySelection(t *testing.T) {
|
| 1505 | 1505 |
container.WithName(ctrName), |
| 1506 | 1506 |
container.WithNetworkMode(netName4), |
| 1507 | 1507 |
container.WithExposedPorts("80"),
|
| 1508 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80"): {{HostPort: "8080"}}}),
|
|
| 1508 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80"): {{HostPort: "8080"}}}),
|
|
| 1509 | 1509 |
container.WithCmd("httpd", "-f"),
|
| 1510 | 1510 |
) |
| 1511 | 1511 |
defer c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -1956,7 +1955,7 @@ func TestDropInForwardChain(t *testing.T) {
|
| 1956 | 1956 |
ctrId := container.Run(ctx, t, c, |
| 1957 | 1957 |
container.WithNetworkMode(netName46), |
| 1958 | 1958 |
container.WithExposedPorts("80"),
|
| 1959 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80"): {{HostPort: hostPort}}}),
|
|
| 1959 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80"): {{HostPort: hostPort}}}),
|
|
| 1960 | 1960 |
container.WithCmd("httpd", "-f"),
|
| 1961 | 1961 |
) |
| 1962 | 1962 |
defer c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"testing" |
| 8 | 8 |
"time" |
| 9 | 9 |
|
| 10 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 10 |
+ networktypes "github.com/moby/moby/api/types/network" |
|
| 11 | 11 |
"github.com/moby/moby/client" |
| 12 | 12 |
"github.com/moby/moby/v2/integration/internal/container" |
| 13 | 13 |
"github.com/moby/moby/v2/integration/internal/network" |
| ... | ... |
@@ -98,13 +98,13 @@ func TestFlakyPortMappedHairpinWindows(t *testing.T) {
|
| 98 | 98 |
serverId := container.Run(ctx, t, c, |
| 99 | 99 |
container.WithNetworkMode(serverNetName), |
| 100 | 100 |
container.WithExposedPorts("80"),
|
| 101 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80"): {{HostIP: netip.IPv4Unspecified()}}}),
|
|
| 101 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80"): {{HostIP: netip.IPv4Unspecified()}}}),
|
|
| 102 | 102 |
container.WithCmd("httpd", "-f"),
|
| 103 | 103 |
) |
| 104 | 104 |
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
|
| 105 | 105 |
|
| 106 | 106 |
inspect := container.Inspect(ctx, t, c, serverId) |
| 107 |
- hostPort := inspect.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 107 |
+ hostPort := inspect.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 108 | 108 |
|
| 109 | 109 |
attachCtx, cancel := context.WithTimeout(ctx, 15*time.Second) |
| 110 | 110 |
defer cancel() |
| ... | ... |
@@ -17,7 +17,6 @@ import ( |
| 17 | 17 |
|
| 18 | 18 |
"github.com/google/go-cmp/cmp/cmpopts" |
| 19 | 19 |
"github.com/moby/moby/api/pkg/stdcopy" |
| 20 |
- containertypes "github.com/moby/moby/api/types/container" |
|
| 21 | 20 |
networktypes "github.com/moby/moby/api/types/network" |
| 22 | 21 |
"github.com/moby/moby/client" |
| 23 | 22 |
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge" |
| ... | ... |
@@ -69,12 +68,12 @@ func TestDisableNAT(t *testing.T) {
|
| 69 | 69 |
name string |
| 70 | 70 |
gwMode4 string |
| 71 | 71 |
gwMode6 string |
| 72 |
- expPortMap containertypes.PortMap |
|
| 72 |
+ expPortMap networktypes.PortMap |
|
| 73 | 73 |
}{
|
| 74 | 74 |
{
|
| 75 | 75 |
name: "defaults", |
| 76 |
- expPortMap: containertypes.PortMap{
|
|
| 77 |
- containertypes.MustParsePort("80/tcp"): []containertypes.PortBinding{
|
|
| 76 |
+ expPortMap: networktypes.PortMap{
|
|
| 77 |
+ networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
|
|
| 78 | 78 |
{HostIP: netip.MustParseAddr("0.0.0.0"), HostPort: "8080"},
|
| 79 | 79 |
{HostIP: netip.MustParseAddr("::"), HostPort: "8080"},
|
| 80 | 80 |
}, |
| ... | ... |
@@ -84,8 +83,8 @@ func TestDisableNAT(t *testing.T) {
|
| 84 | 84 |
name: "nat4 routed6", |
| 85 | 85 |
gwMode4: "nat", |
| 86 | 86 |
gwMode6: "routed", |
| 87 |
- expPortMap: containertypes.PortMap{
|
|
| 88 |
- containertypes.MustParsePort("80/tcp"): []containertypes.PortBinding{
|
|
| 87 |
+ expPortMap: networktypes.PortMap{
|
|
| 88 |
+ networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
|
|
| 89 | 89 |
{HostIP: netip.MustParseAddr("0.0.0.0"), HostPort: "8080"},
|
| 90 | 90 |
{HostIP: netip.MustParseAddr("::"), HostPort: ""},
|
| 91 | 91 |
}, |
| ... | ... |
@@ -95,8 +94,8 @@ func TestDisableNAT(t *testing.T) {
|
| 95 | 95 |
name: "nat6 routed4", |
| 96 | 96 |
gwMode4: "routed", |
| 97 | 97 |
gwMode6: "nat", |
| 98 |
- expPortMap: containertypes.PortMap{
|
|
| 99 |
- containertypes.MustParsePort("80/tcp"): []containertypes.PortBinding{
|
|
| 98 |
+ expPortMap: networktypes.PortMap{
|
|
| 99 |
+ networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
|
|
| 100 | 100 |
{HostIP: netip.MustParseAddr("::"), HostPort: "8080"},
|
| 101 | 101 |
{HostIP: netip.MustParseAddr("0.0.0.0"), HostPort: ""},
|
| 102 | 102 |
}, |
| ... | ... |
@@ -125,7 +124,7 @@ func TestDisableNAT(t *testing.T) {
|
| 125 | 125 |
id := container.Run(ctx, t, c, |
| 126 | 126 |
container.WithNetworkMode(netName), |
| 127 | 127 |
container.WithExposedPorts("80/tcp"),
|
| 128 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}}),
|
|
| 128 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}}}),
|
|
| 129 | 129 |
) |
| 130 | 130 |
defer c.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
|
| 131 | 131 |
|
| ... | ... |
@@ -163,13 +162,13 @@ func TestPortMappedHairpinTCP(t *testing.T) {
|
| 163 | 163 |
serverId := container.Run(ctx, t, c, |
| 164 | 164 |
container.WithNetworkMode(serverNetName), |
| 165 | 165 |
container.WithExposedPorts("80"),
|
| 166 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 166 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 167 | 167 |
container.WithCmd("httpd", "-f"),
|
| 168 | 168 |
) |
| 169 | 169 |
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
|
| 170 | 170 |
|
| 171 | 171 |
inspect := container.Inspect(ctx, t, c, serverId) |
| 172 |
- hostPort := inspect.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 172 |
+ hostPort := inspect.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 173 | 173 |
|
| 174 | 174 |
clientCtx, cancel := context.WithTimeout(ctx, 5*time.Second) |
| 175 | 175 |
defer cancel() |
| ... | ... |
@@ -210,13 +209,13 @@ func TestPortMappedHairpinUDP(t *testing.T) {
|
| 210 | 210 |
serverId := container.Run(ctx, t, c, |
| 211 | 211 |
container.WithNetworkMode(serverNetName), |
| 212 | 212 |
container.WithExposedPorts("54/udp"),
|
| 213 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("54/udp"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 213 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("54/udp"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 214 | 214 |
container.WithCmd("/bin/sh", "-c", "echo 'foobar.internal 192.168.155.23' | dnsd -c - -p 54"),
|
| 215 | 215 |
) |
| 216 | 216 |
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
|
| 217 | 217 |
|
| 218 | 218 |
inspect := container.Inspect(ctx, t, c, serverId) |
| 219 |
- hostPort := inspect.NetworkSettings.Ports[containertypes.MustParsePort("54/udp")][0].HostPort
|
|
| 219 |
+ hostPort := inspect.NetworkSettings.Ports[networktypes.MustParsePort("54/udp")][0].HostPort
|
|
| 220 | 220 |
|
| 221 | 221 |
// nslookup gets an answer quickly from the dns server, but then tries to |
| 222 | 222 |
// query another DNS server (for some unknown reasons) and times out. Hence, |
| ... | ... |
@@ -252,13 +251,13 @@ func TestProxy4To6(t *testing.T) {
|
| 252 | 252 |
serverId := container.Run(ctx, t, c, |
| 253 | 253 |
container.WithNetworkMode(netName), |
| 254 | 254 |
container.WithExposedPorts("80"),
|
| 255 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80"): {{HostIP: netip.MustParseAddr("::1")}}}),
|
|
| 255 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80"): {{HostIP: netip.MustParseAddr("::1")}}}),
|
|
| 256 | 256 |
container.WithCmd("httpd", "-f"),
|
| 257 | 257 |
) |
| 258 | 258 |
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
|
| 259 | 259 |
|
| 260 | 260 |
inspect := container.Inspect(ctx, t, c, serverId) |
| 261 |
- hostPort := inspect.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 261 |
+ hostPort := inspect.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 262 | 262 |
|
| 263 | 263 |
var resp *http.Response |
| 264 | 264 |
addr := "http://[::1]:" + hostPort |
| ... | ... |
@@ -376,7 +375,7 @@ func TestAccessPublishedPortFromHost(t *testing.T) {
|
| 376 | 376 |
serverID := container.Run(ctx, t, c, |
| 377 | 377 |
container.WithName(sanitizeCtrName(t.Name()+"-server")), |
| 378 | 378 |
container.WithExposedPorts("80/tcp"),
|
| 379 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: hostPort}}}),
|
|
| 379 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: hostPort}}}),
|
|
| 380 | 380 |
container.WithCmd("httpd", "-f"),
|
| 381 | 381 |
container.WithNetworkMode(bridgeName)) |
| 382 | 382 |
defer c.ContainerRemove(ctx, serverID, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -456,7 +455,7 @@ func TestAccessPublishedPortFromRemoteHost(t *testing.T) {
|
| 456 | 456 |
serverID := container.Run(ctx, t, c, |
| 457 | 457 |
container.WithName(sanitizeCtrName(t.Name()+"-server")), |
| 458 | 458 |
container.WithExposedPorts("80/tcp"),
|
| 459 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostPort: hostPort}}}),
|
|
| 459 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostPort: hostPort}}}),
|
|
| 460 | 460 |
container.WithCmd("httpd", "-f"),
|
| 461 | 461 |
container.WithNetworkMode(bridgeName)) |
| 462 | 462 |
defer c.ContainerRemove(ctx, serverID, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -554,13 +553,13 @@ func TestAccessPublishedPortFromCtr(t *testing.T) {
|
| 554 | 554 |
serverId := container.Run(ctx, t, c, |
| 555 | 555 |
container.WithNetworkMode(netName), |
| 556 | 556 |
container.WithExposedPorts("80"),
|
| 557 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 557 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {{HostIP: netip.MustParseAddr("0.0.0.0")}}}),
|
|
| 558 | 558 |
container.WithCmd("httpd", "-f"),
|
| 559 | 559 |
) |
| 560 | 560 |
defer c.ContainerRemove(ctx, serverId, client.ContainerRemoveOptions{Force: true})
|
| 561 | 561 |
|
| 562 | 562 |
inspect := container.Inspect(ctx, t, c, serverId) |
| 563 |
- hostPort := inspect.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 563 |
+ hostPort := inspect.NetworkSettings.Ports[networktypes.MustParsePort("80/tcp")][0].HostPort
|
|
| 564 | 564 |
|
| 565 | 565 |
clientCtx, cancel := context.WithTimeout(ctx, 5*time.Second) |
| 566 | 566 |
defer cancel() |
| ... | ... |
@@ -604,8 +603,8 @@ func TestRestartUserlandProxyUnder2MSL(t *testing.T) {
|
| 604 | 604 |
ctrOpts := []func(*container.TestContainerConfig){
|
| 605 | 605 |
container.WithName(ctrName), |
| 606 | 606 |
container.WithExposedPorts("80/tcp"),
|
| 607 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 608 |
- containertypes.MustParsePort("80/tcp"): {{HostPort: "1780"}},
|
|
| 607 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 608 |
+ networktypes.MustParsePort("80/tcp"): {{HostPort: "1780"}},
|
|
| 609 | 609 |
}), |
| 610 | 610 |
container.WithCmd("httpd", "-f"),
|
| 611 | 611 |
container.WithNetworkMode(netName), |
| ... | ... |
@@ -703,8 +702,8 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
|
| 703 | 703 |
container.WithNetworkMode(netName), |
| 704 | 704 |
container.WithName("ctr-"+gwMode),
|
| 705 | 705 |
container.WithExposedPorts("80/tcp"),
|
| 706 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 707 |
- containertypes.MustParsePort("80/tcp"): {},
|
|
| 706 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 707 |
+ networktypes.MustParsePort("80/tcp"): {},
|
|
| 708 | 708 |
}), |
| 709 | 709 |
) |
| 710 | 710 |
t.Cleanup(func() {
|
| ... | ... |
@@ -984,8 +983,8 @@ func TestRoutedNonGateway(t *testing.T) {
|
| 984 | 984 |
ctrId := container.Run(ctx, t, c, |
| 985 | 985 |
container.WithCmd("httpd", "-f"),
|
| 986 | 986 |
container.WithExposedPorts("80/tcp"),
|
| 987 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 988 |
- containertypes.MustParsePort("80/tcp"): {{HostPort: "8080"}},
|
|
| 987 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 988 |
+ networktypes.MustParsePort("80/tcp"): {{HostPort: "8080"}},
|
|
| 989 | 989 |
}), |
| 990 | 990 |
container.WithNetworkMode(natNetName), |
| 991 | 991 |
container.WithNetworkMode(routedNetName), |
| ... | ... |
@@ -1140,8 +1139,8 @@ func TestAccessPublishedPortFromAnotherNetwork(t *testing.T) {
|
| 1140 | 1140 |
container.WithName("server"),
|
| 1141 | 1141 |
container.WithCmd("nc", "-lp", "5000"),
|
| 1142 | 1142 |
container.WithExposedPorts("5000/tcp"),
|
| 1143 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 1144 |
- containertypes.MustParsePort("5000/tcp"): {{HostPort: "5000"}},
|
|
| 1143 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 1144 |
+ networktypes.MustParsePort("5000/tcp"): {{HostPort: "5000"}},
|
|
| 1145 | 1145 |
}), |
| 1146 | 1146 |
container.WithNetworkMode(servnet)) |
| 1147 | 1147 |
defer c.ContainerRemove(ctx, serverID, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -1338,8 +1337,8 @@ func testDirectRemoteAccessOnExposedPort(t *testing.T, ctx context.Context, d *d |
| 1338 | 1338 |
container.WithName(sanitizeCtrName(t.Name()+"-server")), |
| 1339 | 1339 |
container.WithCmd("nc", "-lup", "5000"),
|
| 1340 | 1340 |
container.WithExposedPorts("5000/udp"),
|
| 1341 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 1342 |
- containertypes.MustParsePort("5000/udp"): {{HostPort: hostPort}},
|
|
| 1341 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 1342 |
+ networktypes.MustParsePort("5000/udp"): {{HostPort: hostPort}},
|
|
| 1343 | 1343 |
}), |
| 1344 | 1344 |
container.WithNetworkMode(bridgeName), |
| 1345 | 1345 |
container.WithEndpointSettings(bridgeName, &networktypes.EndpointSettings{
|
| ... | ... |
@@ -1426,8 +1425,8 @@ func TestAccessPortPublishedOnLoopbackAddress(t *testing.T) {
|
| 1426 | 1426 |
container.WithCmd("nc", "-lup", "5000"),
|
| 1427 | 1427 |
container.WithExposedPorts("5000/udp"),
|
| 1428 | 1428 |
// This port is mapped on 127.0.0.2, so it should not be remotely accessible. |
| 1429 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 1430 |
- containertypes.MustParsePort("5000/udp"): {{HostIP: netip.MustParseAddr(loIP), HostPort: hostPort}},
|
|
| 1429 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 1430 |
+ networktypes.MustParsePort("5000/udp"): {{HostIP: netip.MustParseAddr(loIP), HostPort: hostPort}},
|
|
| 1431 | 1431 |
}), |
| 1432 | 1432 |
container.WithNetworkMode(bridgeName)) |
| 1433 | 1433 |
defer c.ContainerRemove(ctx, serverID, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -1539,7 +1538,7 @@ func TestSkipRawRules(t *testing.T) {
|
| 1539 | 1539 |
|
| 1540 | 1540 |
ctrId := container.Run(ctx, t, c, |
| 1541 | 1541 |
container.WithExposedPorts("80/tcp"),
|
| 1542 |
- container.WithPortMap(containertypes.PortMap{containertypes.MustParsePort("80/tcp"): {
|
|
| 1542 |
+ container.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): {
|
|
| 1543 | 1543 |
{HostIP: netip.MustParseAddr("127.0.0.1"), HostPort: "8080"},
|
| 1544 | 1544 |
{HostPort: "8081"},
|
| 1545 | 1545 |
}}), |
| ... | ... |
@@ -1571,10 +1570,10 @@ func TestMixAnyWithSpecificHostAddrs(t *testing.T) {
|
| 1571 | 1571 |
|
| 1572 | 1572 |
ctrId := container.Run(ctx, t, c, |
| 1573 | 1573 |
container.WithExposedPorts("80/"+proto, "81/"+proto, "82/"+proto),
|
| 1574 |
- container.WithPortMap(containertypes.PortMap{
|
|
| 1575 |
- containertypes.MustParsePort("81/" + proto): {{}},
|
|
| 1576 |
- containertypes.MustParsePort("82/" + proto): {{}},
|
|
| 1577 |
- containertypes.MustParsePort("80/" + proto): {{HostIP: netip.MustParseAddr("127.0.0.1")}},
|
|
| 1574 |
+ container.WithPortMap(networktypes.PortMap{
|
|
| 1575 |
+ networktypes.MustParsePort("81/" + proto): {{}},
|
|
| 1576 |
+ networktypes.MustParsePort("82/" + proto): {{}},
|
|
| 1577 |
+ networktypes.MustParsePort("80/" + proto): {{HostIP: netip.MustParseAddr("127.0.0.1")}},
|
|
| 1578 | 1578 |
}), |
| 1579 | 1579 |
) |
| 1580 | 1580 |
defer c.ContainerRemove(ctx, ctrId, client.ContainerRemoveOptions{Force: true})
|
| ... | ... |
@@ -12,6 +12,7 @@ import ( |
| 12 | 12 |
"testing" |
| 13 | 13 |
|
| 14 | 14 |
containertypes "github.com/moby/moby/api/types/container" |
| 15 |
+ "github.com/moby/moby/api/types/network" |
|
| 15 | 16 |
"github.com/moby/moby/client" |
| 16 | 17 |
"github.com/moby/moby/v2/internal/testutil" |
| 17 | 18 |
"github.com/moby/moby/v2/internal/testutil/environment" |
| ... | ... |
@@ -164,7 +165,7 @@ COPY . /static`); err != nil {
|
| 164 | 164 |
// Find out the system assigned port |
| 165 | 165 |
i, err := c.ContainerInspect(context.Background(), b.ID) |
| 166 | 166 |
assert.NilError(t, err) |
| 167 |
- ports, exists := i.NetworkSettings.Ports[containertypes.MustParsePort("80/tcp")]
|
|
| 167 |
+ ports, exists := i.NetworkSettings.Ports[network.MustParsePort("80/tcp")]
|
|
| 168 | 168 |
assert.Assert(t, exists, "unable to find port 80/tcp for %s", ctrName) |
| 169 | 169 |
if len(ports) == 0 {
|
| 170 | 170 |
t.Fatalf("no ports mapped for 80/tcp for %s: %#v", ctrName, i.NetworkSettings.Ports)
|
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"time" |
| 5 | 5 |
|
| 6 | 6 |
dockerspec "github.com/moby/docker-image-spec/specs-go/v1" |
| 7 |
+ "github.com/moby/moby/api/types/network" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
// MinimumDuration puts a minimum on user configured duration. |
| ... | ... |
@@ -28,7 +29,7 @@ type Config struct {
|
| 28 | 28 |
AttachStdin bool // Attach the standard input, makes possible user interaction |
| 29 | 29 |
AttachStdout bool // Attach the standard output |
| 30 | 30 |
AttachStderr bool // Attach the standard error |
| 31 |
- ExposedPorts PortSet `json:",omitempty"` // List of exposed ports |
|
| 31 |
+ ExposedPorts network.PortSet `json:",omitempty"` // List of exposed ports |
|
| 32 | 32 |
Tty bool // Attach standard streams to a tty, including stdin if it is not closed. |
| 33 | 33 |
OpenStdin bool // Open stdin |
| 34 | 34 |
StdinOnce bool // If true, close stdin after the 1 attached client disconnects. |
| ... | ... |
@@ -421,7 +421,7 @@ type HostConfig struct {
|
| 421 | 421 |
ContainerIDFile string // File (path) where the containerId is written |
| 422 | 422 |
LogConfig LogConfig // Configuration of the logs for this container |
| 423 | 423 |
NetworkMode NetworkMode // Network mode to use for the container |
| 424 |
- PortBindings PortMap // Port mapping between the exposed port (container) and the host |
|
| 424 |
+ PortBindings network.PortMap // Port mapping between the exposed port (container) and the host |
|
| 425 | 425 |
RestartPolicy RestartPolicy // Restart policy to be used for the container |
| 426 | 426 |
AutoRemove bool // Automatically remove container when it exits |
| 427 | 427 |
VolumeDriver string // Name of the volume driver used to mount volumes |
| 428 | 428 |
deleted file mode 100644 |
| ... | ... |
@@ -1,346 +0,0 @@ |
| 1 |
-package container |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "iter" |
|
| 7 |
- "net/netip" |
|
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 |
- "unique" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-// NetworkProtocol represents a network protocol for a port. |
|
| 14 |
-type NetworkProtocol string |
|
| 15 |
- |
|
| 16 |
-const ( |
|
| 17 |
- TCP NetworkProtocol = "tcp" |
|
| 18 |
- UDP NetworkProtocol = "udp" |
|
| 19 |
- SCTP NetworkProtocol = "sctp" |
|
| 20 |
-) |
|
| 21 |
- |
|
| 22 |
-// Sentinel port proto value for zero Port and PortRange values. |
|
| 23 |
-var protoZero unique.Handle[NetworkProtocol] |
|
| 24 |
- |
|
| 25 |
-// Port is a type representing a single port number and protocol in the format "<portnum>/[<proto>]". |
|
| 26 |
-// |
|
| 27 |
-// The zero port value, i.e. Port{}, is invalid; use [ParsePort] to create a valid Port value.
|
|
| 28 |
-type Port struct {
|
|
| 29 |
- num uint16 |
|
| 30 |
- proto unique.Handle[NetworkProtocol] |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-// ParsePort parses s as a [Port]. |
|
| 34 |
-// |
|
| 35 |
-// It normalizes the provided protocol such that "80/tcp", "80/TCP", and "80/tCp" are equivalent. |
|
| 36 |
-// If a port number is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 37 |
-func ParsePort(s string) (Port, error) {
|
|
| 38 |
- if s == "" {
|
|
| 39 |
- return Port{}, errors.New("invalid port: value is empty")
|
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- port, proto, _ := strings.Cut(s, "/") |
|
| 43 |
- |
|
| 44 |
- portNum, err := parsePortNumber(port) |
|
| 45 |
- if err != nil {
|
|
| 46 |
- return Port{}, fmt.Errorf("invalid port '%s': %w", port, err)
|
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- normalizedPortProto := normalizePortProto(proto) |
|
| 50 |
- return Port{num: portNum, proto: normalizedPortProto}, nil
|
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// MustParsePort calls [ParsePort](s) and panics on error. |
|
| 54 |
-// |
|
| 55 |
-// It is intended for use in tests with hard-coded strings. |
|
| 56 |
-func MustParsePort(s string) Port {
|
|
| 57 |
- p, err := ParsePort(s) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- panic(err) |
|
| 60 |
- } |
|
| 61 |
- return p |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-// PortFrom returns a [Port] with the given number and protocol. |
|
| 65 |
-// |
|
| 66 |
-// If no protocol is specified (i.e. proto == ""), then PortFrom returns Port{}, false.
|
|
| 67 |
-func PortFrom(num uint16, proto NetworkProtocol) (p Port, ok bool) {
|
|
| 68 |
- if proto == "" {
|
|
| 69 |
- return Port{}, false
|
|
| 70 |
- } |
|
| 71 |
- normalized := normalizePortProto(string(proto)) |
|
| 72 |
- return Port{num: num, proto: normalized}, true
|
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-// Num returns p's port number. |
|
| 76 |
-func (p Port) Num() uint16 {
|
|
| 77 |
- return p.num |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Proto returns p's network protocol. |
|
| 81 |
-func (p Port) Proto() NetworkProtocol {
|
|
| 82 |
- return p.proto.Value() |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-// IsZero reports whether p is the zero value. |
|
| 86 |
-func (p Port) IsZero() bool {
|
|
| 87 |
- return p.proto == protoZero |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-// IsValid reports whether p is an initialized valid port (not the zero value). |
|
| 91 |
-func (p Port) IsValid() bool {
|
|
| 92 |
- return p.proto != protoZero |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-// String returns a string representation of the port in the format "<portnum>/<proto>". |
|
| 96 |
-// If the port is the zero value, it returns "invalid port". |
|
| 97 |
-func (p Port) String() string {
|
|
| 98 |
- switch p.proto {
|
|
| 99 |
- case protoZero: |
|
| 100 |
- return "invalid port" |
|
| 101 |
- default: |
|
| 102 |
- return string(p.AppendTo(nil)) |
|
| 103 |
- } |
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-// AppendText implements [encoding.TextAppender] interface. |
|
| 107 |
-// It is the same as [Port.AppendTo] but returns an error to satisfy the interface. |
|
| 108 |
-func (p Port) AppendText(b []byte) ([]byte, error) {
|
|
| 109 |
- return p.AppendTo(b), nil |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-// AppendTo appends a text encoding of p to b and returns the extended buffer. |
|
| 113 |
-func (p Port) AppendTo(b []byte) []byte {
|
|
| 114 |
- if p.IsZero() {
|
|
| 115 |
- return b |
|
| 116 |
- } |
|
| 117 |
- return fmt.Appendf(b, "%d/%s", p.num, p.proto.Value()) |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 121 |
-func (p Port) MarshalText() ([]byte, error) {
|
|
| 122 |
- return p.AppendText(nil) |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 126 |
-func (p *Port) UnmarshalText(text []byte) error {
|
|
| 127 |
- if len(text) == 0 {
|
|
| 128 |
- *p = Port{}
|
|
| 129 |
- return nil |
|
| 130 |
- } |
|
| 131 |
- |
|
| 132 |
- port, err := ParsePort(string(text)) |
|
| 133 |
- if err != nil {
|
|
| 134 |
- return err |
|
| 135 |
- } |
|
| 136 |
- |
|
| 137 |
- *p = port |
|
| 138 |
- return nil |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-// Range returns a [PortRange] representing the single port. |
|
| 142 |
-func (p Port) Range() PortRange {
|
|
| 143 |
- return PortRange{start: p.num, end: p.num, proto: p.proto}
|
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-// PortSet is a collection of structs indexed by [Port]. |
|
| 147 |
-type PortSet = map[Port]struct{}
|
|
| 148 |
- |
|
| 149 |
-// PortBinding represents a binding between a Host IP address and a Host Port. |
|
| 150 |
-type PortBinding struct {
|
|
| 151 |
- // HostIP is the host IP Address |
|
| 152 |
- HostIP netip.Addr `json:"HostIp"` |
|
| 153 |
- // HostPort is the host port number |
|
| 154 |
- HostPort string `json:"HostPort"` |
|
| 155 |
-} |
|
| 156 |
- |
|
| 157 |
-// PortMap is a collection of [PortBinding] indexed by [Port]. |
|
| 158 |
-type PortMap = map[Port][]PortBinding |
|
| 159 |
- |
|
| 160 |
-// PortRange represents a range of port numbers and a protocol in the format "8000-9000/tcp". |
|
| 161 |
-// |
|
| 162 |
-// The zero port range value, i.e. PortRange{}, is invalid; use [ParsePortRange] to create a valid PortRange value.
|
|
| 163 |
-type PortRange struct {
|
|
| 164 |
- start uint16 |
|
| 165 |
- end uint16 |
|
| 166 |
- proto unique.Handle[NetworkProtocol] |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-// ParsePortRange parses s as a [PortRange]. |
|
| 170 |
-// |
|
| 171 |
-// It normalizes the provided protocol such that "80-90/tcp", "80-90/TCP", and "80-90/tCp" are equivalent. |
|
| 172 |
-// If a port number range is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 173 |
-func ParsePortRange(s string) (PortRange, error) {
|
|
| 174 |
- if s == "" {
|
|
| 175 |
- return PortRange{}, errors.New("invalid port range: value is empty")
|
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- portRange, proto, _ := strings.Cut(s, "/") |
|
| 179 |
- |
|
| 180 |
- start, end, ok := strings.Cut(portRange, "-") |
|
| 181 |
- startVal, err := parsePortNumber(start) |
|
| 182 |
- if err != nil {
|
|
| 183 |
- return PortRange{}, fmt.Errorf("invalid start port '%s': %w", start, err)
|
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- portProto := normalizePortProto(proto) |
|
| 187 |
- |
|
| 188 |
- if !ok || start == end {
|
|
| 189 |
- return PortRange{start: startVal, end: startVal, proto: portProto}, nil
|
|
| 190 |
- } |
|
| 191 |
- |
|
| 192 |
- endVal, err := parsePortNumber(end) |
|
| 193 |
- if err != nil {
|
|
| 194 |
- return PortRange{}, fmt.Errorf("invalid end port '%s': %w", end, err)
|
|
| 195 |
- } |
|
| 196 |
- if endVal < startVal {
|
|
| 197 |
- return PortRange{}, errors.New("invalid port range: " + s)
|
|
| 198 |
- } |
|
| 199 |
- return PortRange{start: startVal, end: endVal, proto: portProto}, nil
|
|
| 200 |
-} |
|
| 201 |
- |
|
| 202 |
-// MustParsePortRange calls [ParsePortRange](s) and panics on error. |
|
| 203 |
-// It is intended for use in tests with hard-coded strings. |
|
| 204 |
-func MustParsePortRange(s string) PortRange {
|
|
| 205 |
- pr, err := ParsePortRange(s) |
|
| 206 |
- if err != nil {
|
|
| 207 |
- panic(err) |
|
| 208 |
- } |
|
| 209 |
- return pr |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-// PortRangeFrom returns a [PortRange] with the given start and end port numbers and protocol. |
|
| 213 |
-// |
|
| 214 |
-// If end < start or no protocol is specified (i.e. proto == ""), then PortRangeFrom returns PortRange{}, false.
|
|
| 215 |
-func PortRangeFrom(start, end uint16, proto NetworkProtocol) (pr PortRange, ok bool) {
|
|
| 216 |
- if end < start || proto == "" {
|
|
| 217 |
- return PortRange{}, false
|
|
| 218 |
- } |
|
| 219 |
- normalized := normalizePortProto(string(proto)) |
|
| 220 |
- return PortRange{start: start, end: end, proto: normalized}, true
|
|
| 221 |
-} |
|
| 222 |
- |
|
| 223 |
-// Start returns pr's start port number. |
|
| 224 |
-func (pr PortRange) Start() uint16 {
|
|
| 225 |
- return pr.start |
|
| 226 |
-} |
|
| 227 |
- |
|
| 228 |
-// End returns pr's end port number. |
|
| 229 |
-func (pr PortRange) End() uint16 {
|
|
| 230 |
- return pr.end |
|
| 231 |
-} |
|
| 232 |
- |
|
| 233 |
-// Proto returns pr's network protocol. |
|
| 234 |
-func (pr PortRange) Proto() NetworkProtocol {
|
|
| 235 |
- return pr.proto.Value() |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-// IsZero reports whether pr is the zero value. |
|
| 239 |
-func (pr PortRange) IsZero() bool {
|
|
| 240 |
- return pr.proto == protoZero |
|
| 241 |
-} |
|
| 242 |
- |
|
| 243 |
-// IsValid reports whether pr is an initialized valid port range (not the zero value). |
|
| 244 |
-func (pr PortRange) IsValid() bool {
|
|
| 245 |
- return pr.proto != protoZero |
|
| 246 |
-} |
|
| 247 |
- |
|
| 248 |
-// String returns a string representation of the port range in the format "<start>-<end>/<proto>" or "<portnum>/<proto>" if start == end. |
|
| 249 |
-// If the port range is the zero value, it returns "invalid port range". |
|
| 250 |
-func (pr PortRange) String() string {
|
|
| 251 |
- switch pr.proto {
|
|
| 252 |
- case protoZero: |
|
| 253 |
- return "invalid port range" |
|
| 254 |
- default: |
|
| 255 |
- return string(pr.AppendTo(nil)) |
|
| 256 |
- } |
|
| 257 |
-} |
|
| 258 |
- |
|
| 259 |
-// AppendText implements [encoding.TextAppender] interface. |
|
| 260 |
-// It is the same as [PortRange.AppendTo] but returns an error to satisfy the interface. |
|
| 261 |
-func (pr PortRange) AppendText(b []byte) ([]byte, error) {
|
|
| 262 |
- return pr.AppendTo(b), nil |
|
| 263 |
-} |
|
| 264 |
- |
|
| 265 |
-// AppendTo appends a text encoding of pr to b and returns the extended buffer. |
|
| 266 |
-func (pr PortRange) AppendTo(b []byte) []byte {
|
|
| 267 |
- if pr.IsZero() {
|
|
| 268 |
- return b |
|
| 269 |
- } |
|
| 270 |
- if pr.start == pr.end {
|
|
| 271 |
- return fmt.Appendf(b, "%d/%s", pr.start, pr.proto.Value()) |
|
| 272 |
- } |
|
| 273 |
- return fmt.Appendf(b, "%d-%d/%s", pr.start, pr.end, pr.proto.Value()) |
|
| 274 |
-} |
|
| 275 |
- |
|
| 276 |
-// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 277 |
-func (pr PortRange) MarshalText() ([]byte, error) {
|
|
| 278 |
- return pr.AppendText(nil) |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 282 |
-func (pr *PortRange) UnmarshalText(text []byte) error {
|
|
| 283 |
- if len(text) == 0 {
|
|
| 284 |
- *pr = PortRange{}
|
|
| 285 |
- return nil |
|
| 286 |
- } |
|
| 287 |
- |
|
| 288 |
- portRange, err := ParsePortRange(string(text)) |
|
| 289 |
- if err != nil {
|
|
| 290 |
- return err |
|
| 291 |
- } |
|
| 292 |
- *pr = portRange |
|
| 293 |
- return nil |
|
| 294 |
-} |
|
| 295 |
- |
|
| 296 |
-// Range returns pr. |
|
| 297 |
-func (pr PortRange) Range() PortRange {
|
|
| 298 |
- return pr |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-// All returns an iterator over all the individual ports in the range. |
|
| 302 |
-// |
|
| 303 |
-// For example: |
|
| 304 |
-// |
|
| 305 |
-// for port := range pr.All() {
|
|
| 306 |
-// // ... |
|
| 307 |
-// } |
|
| 308 |
-func (pr PortRange) All() iter.Seq[Port] {
|
|
| 309 |
- return func(yield func(Port) bool) {
|
|
| 310 |
- for i := uint32(pr.Start()); i <= uint32(pr.End()); i++ {
|
|
| 311 |
- if !yield(Port{num: uint16(i), proto: pr.proto}) {
|
|
| 312 |
- return |
|
| 313 |
- } |
|
| 314 |
- } |
|
| 315 |
- } |
|
| 316 |
-} |
|
| 317 |
- |
|
| 318 |
-// parsePortNumber parses rawPort into an int, unwrapping strconv errors |
|
| 319 |
-// and returning a single "out of range" error for any value outside 0–65535. |
|
| 320 |
-func parsePortNumber(rawPort string) (uint16, error) {
|
|
| 321 |
- if rawPort == "" {
|
|
| 322 |
- return 0, errors.New("value is empty")
|
|
| 323 |
- } |
|
| 324 |
- port, err := strconv.ParseUint(rawPort, 10, 16) |
|
| 325 |
- if err != nil {
|
|
| 326 |
- var numErr *strconv.NumError |
|
| 327 |
- if errors.As(err, &numErr) {
|
|
| 328 |
- err = numErr.Err |
|
| 329 |
- } |
|
| 330 |
- return 0, err |
|
| 331 |
- } |
|
| 332 |
- |
|
| 333 |
- return uint16(port), nil |
|
| 334 |
-} |
|
| 335 |
- |
|
| 336 |
-// normalizePortProto normalizes the protocol string such that "tcp", "TCP", and "tCp" are equivalent. |
|
| 337 |
-// If proto is not specified, it defaults to "tcp". |
|
| 338 |
-func normalizePortProto(proto string) unique.Handle[NetworkProtocol] {
|
|
| 339 |
- if proto == "" {
|
|
| 340 |
- return unique.Make(TCP) |
|
| 341 |
- } |
|
| 342 |
- |
|
| 343 |
- proto = strings.ToLower(proto) |
|
| 344 |
- |
|
| 345 |
- return unique.Make(NetworkProtocol(proto)) |
|
| 346 |
-} |
| ... | ... |
@@ -6,10 +6,13 @@ import ( |
| 6 | 6 |
|
| 7 | 7 |
// NetworkSettings exposes the network settings in the api |
| 8 | 8 |
type NetworkSettings struct {
|
| 9 |
- SandboxID string // SandboxID uniquely represents a container's network stack |
|
| 10 |
- SandboxKey string // SandboxKey identifies the sandbox |
|
| 11 |
- Ports PortMap // Ports is a collection of PortBinding indexed by Port |
|
| 12 |
- Networks map[string]*network.EndpointSettings |
|
| 9 |
+ SandboxID string // SandboxID uniquely represents a container's network stack |
|
| 10 |
+ SandboxKey string // SandboxKey identifies the sandbox |
|
| 11 |
+ |
|
| 12 |
+ // Ports is a collection of [network.PortBinding] indexed by [network.Port] |
|
| 13 |
+ Ports network.PortMap |
|
| 14 |
+ |
|
| 15 |
+ Networks map[string]*network.EndpointSettings |
|
| 13 | 16 |
} |
| 14 | 17 |
|
| 15 | 18 |
// NetworkSettingsSummary provides a summary of container's networks |
| 16 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,346 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "iter" |
|
| 6 |
+ "net/netip" |
|
| 7 |
+ "strconv" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "unique" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// IPProtocol represents a network protocol for a port. |
|
| 13 |
+type IPProtocol string |
|
| 14 |
+ |
|
| 15 |
+const ( |
|
| 16 |
+ TCP IPProtocol = "tcp" |
|
| 17 |
+ UDP IPProtocol = "udp" |
|
| 18 |
+ SCTP IPProtocol = "sctp" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+// Sentinel port proto value for zero Port and PortRange values. |
|
| 22 |
+var protoZero unique.Handle[IPProtocol] |
|
| 23 |
+ |
|
| 24 |
+// Port is a type representing a single port number and protocol in the format "<portnum>/[<proto>]". |
|
| 25 |
+// |
|
| 26 |
+// The zero port value, i.e. Port{}, is invalid; use [ParsePort] to create a valid Port value.
|
|
| 27 |
+type Port struct {
|
|
| 28 |
+ num uint16 |
|
| 29 |
+ proto unique.Handle[IPProtocol] |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+// ParsePort parses s as a [Port]. |
|
| 33 |
+// |
|
| 34 |
+// It normalizes the provided protocol such that "80/tcp", "80/TCP", and "80/tCp" are equivalent. |
|
| 35 |
+// If a port number is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 36 |
+func ParsePort(s string) (Port, error) {
|
|
| 37 |
+ if s == "" {
|
|
| 38 |
+ return Port{}, errors.New("invalid port: value is empty")
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ port, proto, _ := strings.Cut(s, "/") |
|
| 42 |
+ |
|
| 43 |
+ portNum, err := parsePortNumber(port) |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return Port{}, fmt.Errorf("invalid port '%s': %w", port, err)
|
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ normalizedPortProto := normalizePortProto(proto) |
|
| 49 |
+ return Port{num: portNum, proto: normalizedPortProto}, nil
|
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// MustParsePort calls [ParsePort](s) and panics on error. |
|
| 53 |
+// |
|
| 54 |
+// It is intended for use in tests with hard-coded strings. |
|
| 55 |
+func MustParsePort(s string) Port {
|
|
| 56 |
+ p, err := ParsePort(s) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ panic(err) |
|
| 59 |
+ } |
|
| 60 |
+ return p |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// PortFrom returns a [Port] with the given number and protocol. |
|
| 64 |
+// |
|
| 65 |
+// If no protocol is specified (i.e. proto == ""), then PortFrom returns Port{}, false.
|
|
| 66 |
+func PortFrom(num uint16, proto IPProtocol) (p Port, ok bool) {
|
|
| 67 |
+ if proto == "" {
|
|
| 68 |
+ return Port{}, false
|
|
| 69 |
+ } |
|
| 70 |
+ normalized := normalizePortProto(string(proto)) |
|
| 71 |
+ return Port{num: num, proto: normalized}, true
|
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+// Num returns p's port number. |
|
| 75 |
+func (p Port) Num() uint16 {
|
|
| 76 |
+ return p.num |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Proto returns p's network protocol. |
|
| 80 |
+func (p Port) Proto() IPProtocol {
|
|
| 81 |
+ return p.proto.Value() |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// IsZero reports whether p is the zero value. |
|
| 85 |
+func (p Port) IsZero() bool {
|
|
| 86 |
+ return p.proto == protoZero |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// IsValid reports whether p is an initialized valid port (not the zero value). |
|
| 90 |
+func (p Port) IsValid() bool {
|
|
| 91 |
+ return p.proto != protoZero |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// String returns a string representation of the port in the format "<portnum>/<proto>". |
|
| 95 |
+// If the port is the zero value, it returns "invalid port". |
|
| 96 |
+func (p Port) String() string {
|
|
| 97 |
+ switch p.proto {
|
|
| 98 |
+ case protoZero: |
|
| 99 |
+ return "invalid port" |
|
| 100 |
+ default: |
|
| 101 |
+ return string(p.AppendTo(nil)) |
|
| 102 |
+ } |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+// AppendText implements [encoding.TextAppender] interface. |
|
| 106 |
+// It is the same as [Port.AppendTo] but returns an error to satisfy the interface. |
|
| 107 |
+func (p Port) AppendText(b []byte) ([]byte, error) {
|
|
| 108 |
+ return p.AppendTo(b), nil |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+// AppendTo appends a text encoding of p to b and returns the extended buffer. |
|
| 112 |
+func (p Port) AppendTo(b []byte) []byte {
|
|
| 113 |
+ if p.IsZero() {
|
|
| 114 |
+ return b |
|
| 115 |
+ } |
|
| 116 |
+ return fmt.Appendf(b, "%d/%s", p.num, p.proto.Value()) |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 120 |
+func (p Port) MarshalText() ([]byte, error) {
|
|
| 121 |
+ return p.AppendText(nil) |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 125 |
+func (p *Port) UnmarshalText(text []byte) error {
|
|
| 126 |
+ if len(text) == 0 {
|
|
| 127 |
+ *p = Port{}
|
|
| 128 |
+ return nil |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ port, err := ParsePort(string(text)) |
|
| 132 |
+ if err != nil {
|
|
| 133 |
+ return err |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ *p = port |
|
| 137 |
+ return nil |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+// Range returns a [PortRange] representing the single port. |
|
| 141 |
+func (p Port) Range() PortRange {
|
|
| 142 |
+ return PortRange{start: p.num, end: p.num, proto: p.proto}
|
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+// PortSet is a collection of structs indexed by [Port]. |
|
| 146 |
+type PortSet = map[Port]struct{}
|
|
| 147 |
+ |
|
| 148 |
+// PortBinding represents a binding between a Host IP address and a Host Port. |
|
| 149 |
+type PortBinding struct {
|
|
| 150 |
+ // HostIP is the host IP Address |
|
| 151 |
+ HostIP netip.Addr `json:"HostIp"` |
|
| 152 |
+ // HostPort is the host port number |
|
| 153 |
+ HostPort string `json:"HostPort"` |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+// PortMap is a collection of [PortBinding] indexed by [Port]. |
|
| 157 |
+type PortMap = map[Port][]PortBinding |
|
| 158 |
+ |
|
| 159 |
+// PortRange represents a range of port numbers and a protocol in the format "8000-9000/tcp". |
|
| 160 |
+// |
|
| 161 |
+// The zero port range value, i.e. PortRange{}, is invalid; use [ParsePortRange] to create a valid PortRange value.
|
|
| 162 |
+type PortRange struct {
|
|
| 163 |
+ start uint16 |
|
| 164 |
+ end uint16 |
|
| 165 |
+ proto unique.Handle[IPProtocol] |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+// ParsePortRange parses s as a [PortRange]. |
|
| 169 |
+// |
|
| 170 |
+// It normalizes the provided protocol such that "80-90/tcp", "80-90/TCP", and "80-90/tCp" are equivalent. |
|
| 171 |
+// If a port number range is provided, but no protocol, the default ("tcp") protocol is returned.
|
|
| 172 |
+func ParsePortRange(s string) (PortRange, error) {
|
|
| 173 |
+ if s == "" {
|
|
| 174 |
+ return PortRange{}, errors.New("invalid port range: value is empty")
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ portRange, proto, _ := strings.Cut(s, "/") |
|
| 178 |
+ |
|
| 179 |
+ start, end, ok := strings.Cut(portRange, "-") |
|
| 180 |
+ startVal, err := parsePortNumber(start) |
|
| 181 |
+ if err != nil {
|
|
| 182 |
+ return PortRange{}, fmt.Errorf("invalid start port '%s': %w", start, err)
|
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ portProto := normalizePortProto(proto) |
|
| 186 |
+ |
|
| 187 |
+ if !ok || start == end {
|
|
| 188 |
+ return PortRange{start: startVal, end: startVal, proto: portProto}, nil
|
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ endVal, err := parsePortNumber(end) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ return PortRange{}, fmt.Errorf("invalid end port '%s': %w", end, err)
|
|
| 194 |
+ } |
|
| 195 |
+ if endVal < startVal {
|
|
| 196 |
+ return PortRange{}, errors.New("invalid port range: " + s)
|
|
| 197 |
+ } |
|
| 198 |
+ return PortRange{start: startVal, end: endVal, proto: portProto}, nil
|
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+// MustParsePortRange calls [ParsePortRange](s) and panics on error. |
|
| 202 |
+// It is intended for use in tests with hard-coded strings. |
|
| 203 |
+func MustParsePortRange(s string) PortRange {
|
|
| 204 |
+ pr, err := ParsePortRange(s) |
|
| 205 |
+ if err != nil {
|
|
| 206 |
+ panic(err) |
|
| 207 |
+ } |
|
| 208 |
+ return pr |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+// PortRangeFrom returns a [PortRange] with the given start and end port numbers and protocol. |
|
| 212 |
+// |
|
| 213 |
+// If end < start or no protocol is specified (i.e. proto == ""), then PortRangeFrom returns PortRange{}, false.
|
|
| 214 |
+func PortRangeFrom(start, end uint16, proto IPProtocol) (pr PortRange, ok bool) {
|
|
| 215 |
+ if end < start || proto == "" {
|
|
| 216 |
+ return PortRange{}, false
|
|
| 217 |
+ } |
|
| 218 |
+ normalized := normalizePortProto(string(proto)) |
|
| 219 |
+ return PortRange{start: start, end: end, proto: normalized}, true
|
|
| 220 |
+} |
|
| 221 |
+ |
|
| 222 |
+// Start returns pr's start port number. |
|
| 223 |
+func (pr PortRange) Start() uint16 {
|
|
| 224 |
+ return pr.start |
|
| 225 |
+} |
|
| 226 |
+ |
|
| 227 |
+// End returns pr's end port number. |
|
| 228 |
+func (pr PortRange) End() uint16 {
|
|
| 229 |
+ return pr.end |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+// Proto returns pr's network protocol. |
|
| 233 |
+func (pr PortRange) Proto() IPProtocol {
|
|
| 234 |
+ return pr.proto.Value() |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// IsZero reports whether pr is the zero value. |
|
| 238 |
+func (pr PortRange) IsZero() bool {
|
|
| 239 |
+ return pr.proto == protoZero |
|
| 240 |
+} |
|
| 241 |
+ |
|
| 242 |
+// IsValid reports whether pr is an initialized valid port range (not the zero value). |
|
| 243 |
+func (pr PortRange) IsValid() bool {
|
|
| 244 |
+ return pr.proto != protoZero |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+// String returns a string representation of the port range in the format "<start>-<end>/<proto>" or "<portnum>/<proto>" if start == end. |
|
| 248 |
+// If the port range is the zero value, it returns "invalid port range". |
|
| 249 |
+func (pr PortRange) String() string {
|
|
| 250 |
+ switch pr.proto {
|
|
| 251 |
+ case protoZero: |
|
| 252 |
+ return "invalid port range" |
|
| 253 |
+ default: |
|
| 254 |
+ return string(pr.AppendTo(nil)) |
|
| 255 |
+ } |
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+// AppendText implements [encoding.TextAppender] interface. |
|
| 259 |
+// It is the same as [PortRange.AppendTo] but returns an error to satisfy the interface. |
|
| 260 |
+func (pr PortRange) AppendText(b []byte) ([]byte, error) {
|
|
| 261 |
+ return pr.AppendTo(b), nil |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// AppendTo appends a text encoding of pr to b and returns the extended buffer. |
|
| 265 |
+func (pr PortRange) AppendTo(b []byte) []byte {
|
|
| 266 |
+ if pr.IsZero() {
|
|
| 267 |
+ return b |
|
| 268 |
+ } |
|
| 269 |
+ if pr.start == pr.end {
|
|
| 270 |
+ return fmt.Appendf(b, "%d/%s", pr.start, pr.proto.Value()) |
|
| 271 |
+ } |
|
| 272 |
+ return fmt.Appendf(b, "%d-%d/%s", pr.start, pr.end, pr.proto.Value()) |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+// MarshalText implements [encoding.TextMarshaler] interface. |
|
| 276 |
+func (pr PortRange) MarshalText() ([]byte, error) {
|
|
| 277 |
+ return pr.AppendText(nil) |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+// UnmarshalText implements [encoding.TextUnmarshaler] interface. |
|
| 281 |
+func (pr *PortRange) UnmarshalText(text []byte) error {
|
|
| 282 |
+ if len(text) == 0 {
|
|
| 283 |
+ *pr = PortRange{}
|
|
| 284 |
+ return nil |
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ portRange, err := ParsePortRange(string(text)) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return err |
|
| 290 |
+ } |
|
| 291 |
+ *pr = portRange |
|
| 292 |
+ return nil |
|
| 293 |
+} |
|
| 294 |
+ |
|
| 295 |
+// Range returns pr. |
|
| 296 |
+func (pr PortRange) Range() PortRange {
|
|
| 297 |
+ return pr |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+// All returns an iterator over all the individual ports in the range. |
|
| 301 |
+// |
|
| 302 |
+// For example: |
|
| 303 |
+// |
|
| 304 |
+// for port := range pr.All() {
|
|
| 305 |
+// // ... |
|
| 306 |
+// } |
|
| 307 |
+func (pr PortRange) All() iter.Seq[Port] {
|
|
| 308 |
+ return func(yield func(Port) bool) {
|
|
| 309 |
+ for i := uint32(pr.Start()); i <= uint32(pr.End()); i++ {
|
|
| 310 |
+ if !yield(Port{num: uint16(i), proto: pr.proto}) {
|
|
| 311 |
+ return |
|
| 312 |
+ } |
|
| 313 |
+ } |
|
| 314 |
+ } |
|
| 315 |
+} |
|
| 316 |
+ |
|
| 317 |
+// parsePortNumber parses rawPort into an int, unwrapping strconv errors |
|
| 318 |
+// and returning a single "out of range" error for any value outside 0–65535. |
|
| 319 |
+func parsePortNumber(rawPort string) (uint16, error) {
|
|
| 320 |
+ if rawPort == "" {
|
|
| 321 |
+ return 0, errors.New("value is empty")
|
|
| 322 |
+ } |
|
| 323 |
+ port, err := strconv.ParseUint(rawPort, 10, 16) |
|
| 324 |
+ if err != nil {
|
|
| 325 |
+ var numErr *strconv.NumError |
|
| 326 |
+ if errors.As(err, &numErr) {
|
|
| 327 |
+ err = numErr.Err |
|
| 328 |
+ } |
|
| 329 |
+ return 0, err |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ return uint16(port), nil |
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+// normalizePortProto normalizes the protocol string such that "tcp", "TCP", and "tCp" are equivalent. |
|
| 336 |
+// If proto is not specified, it defaults to "tcp". |
|
| 337 |
+func normalizePortProto(proto string) unique.Handle[IPProtocol] {
|
|
| 338 |
+ if proto == "" {
|
|
| 339 |
+ return unique.Make(TCP) |
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ proto = strings.ToLower(proto) |
|
| 343 |
+ |
|
| 344 |
+ return unique.Make(IPProtocol(proto)) |
|
| 345 |
+} |