From ced65657aadb21fbdb524a45bc78f2e9c9f770c4 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 12 Mar 2024 08:56:05 +0100 Subject: [PATCH] normalization creates implicit `default` network only when used by some service Signed-off-by: Nicolas De Loof --- loader/normalize.go | 47 ++++++++++++++++++++++------ loader/normalize_test.go | 67 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 13 deletions(-) diff --git a/loader/normalize.go b/loader/normalize.go index 551b4be6..5f7c8e85 100644 --- a/loader/normalize.go +++ b/loader/normalize.go @@ -26,18 +26,12 @@ import ( // Normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) { - dict["networks"] = normalizeNetworks(dict) + normalizeNetworks(dict) if d, ok := dict["services"]; ok { services := d.(map[string]any) for name, s := range services { service := s.(map[string]any) - _, hasNetworks := service["networks"] - _, hasNetworkMode := service["network_mode"] - if !hasNetworks && !hasNetworkMode { - // Service without explicit network attachment are implicitly exposed on default network - service["networks"] = map[string]any{"default": nil} - } if service["pull_policy"] == types.PullPolicyIfNotPresent { service["pull_policy"] = types.PullPolicyMissing @@ -137,18 +131,51 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) { return dict, nil } -func normalizeNetworks(dict map[string]any) map[string]any { +func normalizeNetworks(dict map[string]any) { var networks map[string]any if n, ok := dict["networks"]; ok { networks = n.(map[string]any) } else { networks = map[string]any{} } - if _, ok := networks["default"]; !ok { + + // implicit `default` network must be introduced only if actually used by some service + usesDefaultNetwork := false + + if s, ok := dict["services"]; ok { + services := s.(map[string]any) + for name, se := range services { + service := se.(map[string]any) + if _, ok := service["network_mode"]; ok { + continue + } + if n, ok := service["networks"]; !ok { + // If none explicitly declared, service is connected to default network + service["networks"] = map[string]any{"default": nil} + usesDefaultNetwork = true + } else { + net := n.(map[string]any) + if len(net) == 0 { + // networks section declared but empty (corner case) + service["networks"] = map[string]any{"default": nil} + usesDefaultNetwork = true + } else if _, ok := net["default"]; ok { + usesDefaultNetwork = true + } + } + services[name] = service + } + dict["services"] = services + } + + if _, ok := networks["default"]; !ok && usesDefaultNetwork { // If not declared explicitly, Compose model involves an implicit "default" network networks["default"] = nil } - return networks + + if len(networks) > 0 { + dict["networks"] = networks + } } func resolve(a any, fn func(s string) (string, bool)) (any, bool) { diff --git a/loader/normalize_test.go b/loader/normalize_test.go index 09f6615d..5e41d502 100644 --- a/loader/normalize_test.go +++ b/loader/normalize_test.go @@ -88,9 +88,6 @@ volumes: expected := ` name: myProject -networks: - default: - name: myProject_default volumes: myExternalVol: name: myExternalVol @@ -275,3 +272,67 @@ networks: assert.NilError(t, err) assert.DeepEqual(t, expect, model) } + +func TestNormalizeDefaultNetwork(t *testing.T) { + project := ` +name: myProject +services: + test: + image: test +` + + expected := ` +name: myProject +networks: + default: + name: myProject_default +services: + test: + image: test + networks: + default: null +` + var model map[string]any + err := yaml.Unmarshal([]byte(project), &model) + assert.NilError(t, err) + model, err = Normalize(model, nil) + assert.NilError(t, err) + + var expect map[string]any + err = yaml.Unmarshal([]byte(expected), &expect) + assert.NilError(t, err) + assert.DeepEqual(t, expect, model) +} + +func TestNormalizeCustomNetwork(t *testing.T) { + project := ` +name: myProject +services: + test: + networks: + my_network: null +networks: + my_network: null +` + + expected := ` +name: myProject +networks: + my_network: + name: myProject_my_network +services: + test: + networks: + my_network: null +` + var model map[string]any + err := yaml.Unmarshal([]byte(project), &model) + assert.NilError(t, err) + model, err = Normalize(model, nil) + assert.NilError(t, err) + + var expect map[string]any + err = yaml.Unmarshal([]byte(expected), &expect) + assert.NilError(t, err) + assert.DeepEqual(t, expect, model) +}