mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat: follow-up compose 'ports' support: use ingress mode by default (closes #81)
This commit is contained in:
@@ -102,5 +102,5 @@ _lint:
|
|||||||
golangci-lint run $(ARGS)
|
golangci-lint run $(ARGS)
|
||||||
|
|
||||||
.PHONY: docs-image-push
|
.PHONY: docs-image-push
|
||||||
docs-image:
|
docs-image-push:
|
||||||
docker buildx build --push --platform linux/amd64,linux/arm64 -t "$(DOCS_IMAGE)" ./docs
|
docker buildx build --push --platform linux/amd64,linux/arm64 -t "$(DOCS_IMAGE)" ./docs
|
||||||
|
|||||||
+1
-18
@@ -34,23 +34,6 @@ type PortSpec struct {
|
|||||||
Mode string
|
Mode string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortSpec) isHTTP() bool {
|
|
||||||
return p.Protocol == ProtocolHTTP || p.Protocol == ProtocolHTTPS
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdjustUncloudMode makes adjustments for uncloud compatibility
|
|
||||||
func (p *PortSpec) AdjustUncloudMode() {
|
|
||||||
if p.Protocol == "" {
|
|
||||||
p.Protocol = "tcp"
|
|
||||||
}
|
|
||||||
if p.Mode == "" {
|
|
||||||
p.Mode = PortModeIngress
|
|
||||||
}
|
|
||||||
if p.Mode == PortModeIngress && !p.isHTTP() && p.PublishedPort != 0 {
|
|
||||||
p.Mode = PortModeHost
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PortSpec) Validate() error {
|
func (p *PortSpec) Validate() error {
|
||||||
if p.ContainerPort == 0 {
|
if p.ContainerPort == 0 {
|
||||||
return fmt.Errorf("container port must be non-zero")
|
return fmt.Errorf("container port must be non-zero")
|
||||||
@@ -73,7 +56,7 @@ func (p *PortSpec) Validate() error {
|
|||||||
return fmt.Errorf("host IP cannot be specified in %s mode", PortModeIngress)
|
return fmt.Errorf("host IP cannot be specified in %s mode", PortModeIngress)
|
||||||
}
|
}
|
||||||
if p.Hostname != "" {
|
if p.Hostname != "" {
|
||||||
if !p.isHTTP() {
|
if p.Protocol != ProtocolHTTP && p.Protocol != ProtocolHTTPS {
|
||||||
return fmt.Errorf("hostname is only valid with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS)
|
return fmt.Errorf("hostname is only valid with '%s' or '%s' protocols", ProtocolHTTP, ProtocolHTTPS)
|
||||||
}
|
}
|
||||||
if err := validateHostname(p.Hostname); err != nil {
|
if err := validateHostname(p.Hostname); err != nil {
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package compose
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PortsExtensionKey = "x-ports"
|
const PortsExtensionKey = "x-ports"
|
||||||
@@ -20,7 +22,8 @@ func transformServicesPortsExtension(project *types.Project) (*types.Project, er
|
|||||||
hasXPorts := service.Extensions[PortsExtensionKey] != nil
|
hasXPorts := service.Extensions[PortsExtensionKey] != nil
|
||||||
|
|
||||||
if hasStandardPorts && hasXPorts {
|
if hasStandardPorts && hasXPorts {
|
||||||
return service, fmt.Errorf("service %q cannot specify both 'ports' and 'x-ports' directives, use only one", name)
|
return service, fmt.Errorf("service %q cannot specify both 'ports' and 'x-ports' directives, use only one",
|
||||||
|
name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,7 +35,7 @@ func transformServicesPortsExtension(project *types.Project) (*types.Project, er
|
|||||||
// Convert standard ports directly to api.PortSpec
|
// Convert standard ports directly to api.PortSpec
|
||||||
specs, err = convertStandardPortsToPortSpecs(service.Ports)
|
specs, err = convertStandardPortsToPortSpecs(service.Ports)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return service, fmt.Errorf("convert standard ports for service %q: %w", name, err)
|
return service, fmt.Errorf("convert standard 'ports' for service '%s': %w", name, err)
|
||||||
}
|
}
|
||||||
} else if hasXPorts {
|
} else if hasXPorts {
|
||||||
// Use existing x-ports string-based processing for backward compatibility
|
// Use existing x-ports string-based processing for backward compatibility
|
||||||
@@ -82,8 +85,23 @@ func convertServicePortConfigToPortSpec(port types.ServicePortConfig) (api.PortS
|
|||||||
Protocol: port.Protocol,
|
Protocol: port.Protocol,
|
||||||
Mode: port.Mode,
|
Mode: port.Mode,
|
||||||
}
|
}
|
||||||
|
// Compose parser sets the default protocol to "tcp" and mode to "ingress". We still explicitly set these values
|
||||||
|
// to avoid relying on implicit behavior and improve code robustness.
|
||||||
|
if spec.Protocol == "" {
|
||||||
|
spec.Protocol = api.ProtocolTCP
|
||||||
|
}
|
||||||
|
if spec.Mode == "" {
|
||||||
|
spec.Mode = api.PortModeIngress
|
||||||
|
}
|
||||||
|
|
||||||
// Set published port if specified
|
// Set published port if specified
|
||||||
if port.Published != "" {
|
if port.Published != "" {
|
||||||
|
if strings.Contains(port.Published, "-") {
|
||||||
|
// 'a-b:x' format is not automatically expanded by the compose parser and our PortSpec does not support port
|
||||||
|
// ranges for now.
|
||||||
|
return spec, fmt.Errorf("port range '%s' for published port is not supported, use a single port",
|
||||||
|
port.Published)
|
||||||
|
}
|
||||||
publishedPort, err := strconv.ParseUint(port.Published, 10, 16)
|
publishedPort, err := strconv.ParseUint(port.Published, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return spec, fmt.Errorf("invalid published port %q: %w", port.Published, err)
|
return spec, fmt.Errorf("invalid published port %q: %w", port.Published, err)
|
||||||
@@ -100,9 +118,6 @@ func convertServicePortConfigToPortSpec(port types.ServicePortConfig) (api.PortS
|
|||||||
spec.HostIP = hostIP
|
spec.HostIP = hostIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply defaults according to uncloud
|
|
||||||
spec.AdjustUncloudMode()
|
|
||||||
|
|
||||||
// Validate the resulting spec
|
// Validate the resulting spec
|
||||||
if err := spec.Validate(); err != nil {
|
if err := spec.Validate(); err != nil {
|
||||||
return spec, fmt.Errorf("invalid port configuration: %w", err)
|
return spec, fmt.Errorf("invalid port configuration: %w", err)
|
||||||
|
|||||||
@@ -22,14 +22,16 @@ func TestConvertStandardPortsToPortSpecs(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "multiple ports",
|
name: "multiple ports",
|
||||||
ports: []types.ServicePortConfig{
|
ports: []types.ServicePortConfig{
|
||||||
{Target: 8080, Published: "80", Protocol: "tcp"},
|
{Target: 8080, Published: "80", Protocol: "tcp", Mode: "ingress"},
|
||||||
{Target: 8443, Published: "443", Protocol: "tcp", Mode: "host"},
|
{Target: 8443, Published: "443", Protocol: "tcp", Mode: "host"},
|
||||||
{Target: 5353, Published: "53", Protocol: "udp"},
|
{Target: 5353, Published: "53", Protocol: "udp"},
|
||||||
|
{Target: 2222, Published: "22"},
|
||||||
},
|
},
|
||||||
expected: []api.PortSpec{
|
expected: []api.PortSpec{
|
||||||
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "ingress"},
|
||||||
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "host"},
|
||||||
{ContainerPort: 5353, PublishedPort: 53, Protocol: "udp", Mode: "host"},
|
{ContainerPort: 5353, PublishedPort: 53, Protocol: "udp", Mode: "ingress"},
|
||||||
|
{ContainerPort: 2222, PublishedPort: 22, Protocol: "tcp", Mode: "ingress"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -103,7 +105,7 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
ContainerPort: 8080,
|
ContainerPort: 8080,
|
||||||
PublishedPort: 80,
|
PublishedPort: 80,
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
Mode: "host",
|
Mode: "ingress",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -146,7 +148,7 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
ContainerPort: 5353,
|
ContainerPort: 5353,
|
||||||
PublishedPort: 53,
|
PublishedPort: 53,
|
||||||
Protocol: "udp",
|
Protocol: "udp",
|
||||||
Mode: "host",
|
Mode: "ingress",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -167,7 +169,7 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "HTTP protocol stays in ingress mode",
|
name: "HTTP protocol",
|
||||||
port: types.ServicePortConfig{
|
port: types.ServicePortConfig{
|
||||||
Target: 8080,
|
Target: 8080,
|
||||||
Published: "80",
|
Published: "80",
|
||||||
@@ -181,7 +183,7 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "HTTPS protocol stays in ingress mode",
|
name: "HTTPS protocol",
|
||||||
port: types.ServicePortConfig{
|
port: types.ServicePortConfig{
|
||||||
Target: 8080,
|
Target: 8080,
|
||||||
Published: "443",
|
Published: "443",
|
||||||
@@ -219,6 +221,13 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: "container port must be non-zero",
|
wantErr: "container port must be non-zero",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "missing container port",
|
||||||
|
port: types.ServicePortConfig{
|
||||||
|
Published: "8000-9000",
|
||||||
|
},
|
||||||
|
wantErr: "port range '8000-9000' for published port is not supported",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -227,8 +236,7 @@ func TestConvertServicePortConfigToPortSpec(t *testing.T) {
|
|||||||
|
|
||||||
result, err := convertServicePortConfigToPortSpec(tt.port)
|
result, err := convertServicePortConfigToPortSpec(tt.port)
|
||||||
if tt.wantErr != "" {
|
if tt.wantErr != "" {
|
||||||
require.Error(t, err)
|
assert.ErrorContains(t, err, tt.wantErr)
|
||||||
assert.Contains(t, err.Error(), tt.wantErr)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,9 +343,9 @@ services:
|
|||||||
- "53:5353/udp"
|
- "53:5353/udp"
|
||||||
`,
|
`,
|
||||||
expected: []api.PortSpec{
|
expected: []api.PortSpec{
|
||||||
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "ingress"},
|
||||||
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "ingress"},
|
||||||
{ContainerPort: 5353, PublishedPort: 53, Protocol: "udp", Mode: "host"},
|
{ContainerPort: 5353, PublishedPort: 53, Protocol: "udp", Mode: "ingress"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -357,7 +365,7 @@ services:
|
|||||||
mode: host
|
mode: host
|
||||||
`,
|
`,
|
||||||
expected: []api.PortSpec{
|
expected: []api.PortSpec{
|
||||||
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8080, PublishedPort: 80, Protocol: "tcp", Mode: "ingress"},
|
||||||
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "host"},
|
{ContainerPort: 8443, PublishedPort: 443, Protocol: "tcp", Mode: "host"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user