diff --git a/override/uncity.go b/override/uncity.go index 85e52b131..16b4cb358 100644 --- a/override/uncity.go +++ b/override/uncity.go @@ -36,6 +36,7 @@ func init() { unique["services.*.expose"] = exposeIndexer unique["services.*.secrets"] = mountIndexer("/run/secrets") unique["services.*.configs"] = mountIndexer("") + unique["services.*.ports"] = portIndexer } // EnforceUnicity removes redefinition of elements declared in a sequence @@ -136,3 +137,33 @@ func mountIndexer(defaultPath string) indexer { } } } + +func portIndexer(y any, p tree.Path) (string, error) { + switch value := y.(type) { + case map[string]any: + target, ok := value["target"].(int) + if !ok { + return "", fmt.Errorf("service ports %s is missing a target port", p) + } + published, ok := value["published"].(string) + if !ok { + return "", fmt.Errorf("service ports %s is missing a published port", p) + } + host, ok := value["host_ip"].(string) + if !ok { + host = "0.0.0.0" + } + protocol, ok := value["protocol"].(string) + if !ok { + protocol = "tcp" + } + mode, ok := value["mode"].(string) + if !ok { + mode = "ingress" + } + return fmt.Sprintf("%s:%s:%d/%s/%s", host, published, target, protocol, mode), nil + case string: + return value, nil + } + return "", nil +} diff --git a/override/uncity_test.go b/override/uncity_test.go index 3ad46f154..189489afc 100644 --- a/override/uncity_test.go +++ b/override/uncity_test.go @@ -66,6 +66,74 @@ services: `) } +func Test_PortsShortUnicity(t *testing.T) { + assertUnicity(t, ` +services: + test: + image: foo + ports: + - "9080:80" + - "9081:81" + - "9080:80" + - "5000" + - "6060:6060/udp" + - "9080:6060/udp" +`, ` +services: + test: + image: foo + ports: + - "9080:80" + - "9081:81" + - "5000" + - "6060:6060/udp" + - "9080:6060/udp" +`) +} + +func Test_PortsLongtUnicity(t *testing.T) { + assertUnicity(t, ` +services: + test: + image: foo + ports: + - target: 80 + host_ip: 127.0.0.1 + published: "8080" + protocol: tcp + mode: host + - target: 81 + published: "8080" + protocol: tcp + - target: 80 + host_ip: 127.0.0.1 + published: "8080" + protocol: tcp + mode: ingress + - target: 81 + published: "8080" + protocol: tcp +`, ` +services: + test: + image: foo + ports: + - target: 80 + host_ip: 127.0.0.1 + published: "8080" + protocol: tcp + mode: host + - target: 81 + published: "8080" + protocol: tcp + - target: 80 + host_ip: 127.0.0.1 + published: "8080" + protocol: tcp + mode: ingress +`) +} + func assertUnicity(t *testing.T, before string, expected string) { got, err := EnforceUnicity(unmarshall(t, before)) assert.NilError(t, err)