From 75fdbaf2f4d5af9d72652fd6acd2628363114fa2 Mon Sep 17 00:00:00 2001 From: Pasha Sviderski Date: Fri, 22 Aug 2025 15:34:59 +1000 Subject: [PATCH] chore: relax ports+Caddy spec validation to allow host mode ports --- pkg/api/service.go | 23 +++++++--- pkg/api/service_test.go | 99 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/pkg/api/service.go b/pkg/api/service.go index 545df3cc..68514d49 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -131,12 +131,6 @@ func (s *ServiceSpec) Validate() error { } } - // Validate that Caddy and Ports are not used together. - if s.Caddy != nil && strings.TrimSpace(s.Caddy.Config) != "" && len(s.Ports) > 0 { - return fmt.Errorf("ports and Caddy configuration cannot be specified simultaneously: " + - "Caddy config is auto-generated from ports, use only one of them") - } - for _, p := range s.Ports { if (p.Mode == "" || p.Mode == PortModeIngress) && p.Protocol != ProtocolHTTP && p.Protocol != ProtocolHTTPS { @@ -146,6 +140,23 @@ func (s *ServiceSpec) Validate() error { // TODO: validate there is no conflict between ports. + // Validate that Caddy and Ports are not used together, unless all ports are host mode. + if s.Caddy != nil && strings.TrimSpace(s.Caddy.Config) != "" && len(s.Ports) > 0 { + // Check if all ports are in host mode. + hasIngressPort := false + for _, p := range s.Ports { + if p.Mode == "" || p.Mode == PortModeIngress { + hasIngressPort = true + break + } + } + if hasIngressPort { + return fmt.Errorf("ingress ports and Caddy configuration cannot be specified simultaneously: " + + "Caddy config is auto-generated from ingress ports, use only one of them. " + + "Host mode ports can be used with Caddy config") + } + } + volumeNames := make(map[string]struct{}) for _, v := range s.Volumes { if err := v.Validate(); err != nil { diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index 3cafd79c..5b530290 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -71,7 +71,7 @@ func TestServiceSpec_Validate_CaddyAndPorts(t *testing.T) { wantErr: "", }, { - name: "invalid with both Caddy and Ports", + name: "invalid with Caddy and Ports (default mode is ingress)", spec: ServiceSpec{ Name: "test", Container: ContainerSpec{ @@ -84,10 +84,105 @@ func TestServiceSpec_Validate_CaddyAndPorts(t *testing.T) { { ContainerPort: 80, Protocol: ProtocolHTTP, + // Mode is empty, defaults to ingress }, }, }, - wantErr: "ports and Caddy configuration cannot be specified simultaneously", + wantErr: "ingress ports and Caddy configuration cannot be specified simultaneously", + }, + { + name: "invalid with both Caddy and ingress Ports", + spec: ServiceSpec{ + Name: "test", + Container: ContainerSpec{ + Image: "nginx:latest", + }, + Caddy: &CaddySpec{ + Config: "example.com {\n reverse_proxy :8080\n}", + }, + Ports: []PortSpec{ + { + ContainerPort: 80, + Protocol: ProtocolHTTP, + Mode: PortModeIngress, + }, + }, + }, + wantErr: "ingress ports and Caddy configuration cannot be specified simultaneously", + }, + { + name: "valid with Caddy and host mode Ports", + spec: ServiceSpec{ + Name: "test", + Container: ContainerSpec{ + Image: "nginx:latest", + }, + Caddy: &CaddySpec{ + Config: "example.com {\n reverse_proxy :8080\n}", + }, + Ports: []PortSpec{ + { + ContainerPort: 3306, + PublishedPort: 3306, + Protocol: ProtocolTCP, + Mode: PortModeHost, + }, + }, + }, + wantErr: "", + }, + { + name: "invalid with Caddy and mixed mode Ports", + spec: ServiceSpec{ + Name: "test", + Container: ContainerSpec{ + Image: "nginx:latest", + }, + Caddy: &CaddySpec{ + Config: "example.com {\n reverse_proxy :8080\n}", + }, + Ports: []PortSpec{ + { + ContainerPort: 3306, + PublishedPort: 3306, + Protocol: ProtocolTCP, + Mode: PortModeHost, + }, + { + ContainerPort: 80, + Protocol: ProtocolHTTP, + Mode: PortModeIngress, + }, + }, + }, + wantErr: "ingress ports and Caddy configuration cannot be specified simultaneously", + }, + { + name: "valid with Caddy and multiple host mode Ports", + spec: ServiceSpec{ + Name: "test", + Container: ContainerSpec{ + Image: "nginx:latest", + }, + Caddy: &CaddySpec{ + Config: "example.com {\n reverse_proxy :8080\n}", + }, + Ports: []PortSpec{ + { + ContainerPort: 3306, + PublishedPort: 3306, + Protocol: ProtocolTCP, + Mode: PortModeHost, + }, + { + ContainerPort: 5432, + PublishedPort: 5432, + Protocol: ProtocolTCP, + Mode: PortModeHost, + }, + }, + }, + wantErr: "", }, }