From 5a8e67720466339ad22c8bac1249f97e0e73e831 Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:15:07 +0100 Subject: [PATCH] add custom merge function for IPAM config Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- go.mod | 2 +- override/merge.go | 36 +++++++++++++ override/merge_networks_test.go | 95 +++++++++++++++++++++++++++++++-- override/uncity.go | 2 + 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 97be7582..8f29e4ec 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/docker/go-units v0.5.0 github.com/google/go-cmp v0.5.9 github.com/mattn/go-shellwords v1.0.12 + github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 @@ -22,7 +23,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect diff --git a/override/merge.go b/override/merge.go index 5d7521f3..2d2d7543 100644 --- a/override/merge.go +++ b/override/merge.go @@ -39,6 +39,7 @@ type merger func(any, any, tree.Path) (any, error) var mergeSpecials = map[tree.Path]merger{} func init() { + mergeSpecials["networks.*.ipam.config"] = mergeIPAMConfig mergeSpecials["services.*.annotations"] = mergeToSequence mergeSpecials["services.*.build"] = mergeBuild mergeSpecials["services.*.build.args"] = mergeToSequence @@ -197,6 +198,41 @@ func mergeUlimit(_ any, o any, p tree.Path) (any, error) { return o, nil } +func mergeIPAMConfig(c any, o any, path tree.Path) (any, error) { + var ipamConfigs []any + for _, original := range c.([]any) { + right := convertIntoMapping(original, nil) + for _, override := range o.([]any) { + left := convertIntoMapping(override, nil) + if left["subnet"] != right["subnet"] { + // check if left is already in ipamConfigs, add it if not and continue with the next config + if !slices.ContainsFunc(ipamConfigs, func(a any) bool { + return a.(map[string]any)["subnet"] == left["subnet"] + }) { + ipamConfigs = append(ipamConfigs, left) + continue + } + } + merged, err := mergeMappings(right, left, path) + if err != nil { + return nil, err + } + // find index of potential previous config with the same subnet in ipamConfigs + indexIfExist := slices.IndexFunc(ipamConfigs, func(a any) bool { + return a.(map[string]any)["subnet"] == merged["subnet"] + }) + // if a previous config is already in ipamConfigs, replace it + if indexIfExist >= 0 { + ipamConfigs[indexIfExist] = merged + } else { + // or add the new config to ipamConfigs + ipamConfigs = append(ipamConfigs, merged) + } + } + } + return ipamConfigs, nil +} + func convertIntoMapping(a any, defaultValue any) map[string]any { switch v := a.(type) { case map[string]any: diff --git a/override/merge_networks_test.go b/override/merge_networks_test.go index a59da008..4603a7a4 100644 --- a/override/merge_networks_test.go +++ b/override/merge_networks_test.go @@ -20,7 +20,7 @@ import ( "testing" ) -func Test_mergeYamlNetworkSequence(t *testing.T) { +func Test_mergeYamlServiceNetworkSequence(t *testing.T) { assertMergeYaml(t, ` services: test: @@ -43,7 +43,7 @@ services: `) } -func Test_mergeYamlNetworksMapping(t *testing.T) { +func Test_mergeYamlServiceNetworksMapping(t *testing.T) { assertMergeYaml(t, ` services: test: @@ -92,7 +92,7 @@ services: `) } -func Test_mergeYamlNetworkstMixed(t *testing.T) { +func Test_mergeYamlServiceNetworksMixed(t *testing.T) { assertMergeYaml(t, ` services: test: @@ -137,3 +137,92 @@ services: - alias3 `) } + +func Test_mergeYamlNetworks(t *testing.T) { + assertMergeYaml(t, ` +services: + test: + image: foo +networks: + network1: + ipam: + config: + - subnet: 172.28.0.0/16 + ip_range: 172.28.5.0/24 + gateway: 172.28.5.254 + aux_addresses: + host1: 172.28.1.5 + host2: 172.28.1.6 + host3: 172.28.1.7 + options: + foo: bar + baz: "0" + labels: + com.example.description: "Financial transaction network" + com.example.department: "Finance" + com.example.label-with-empty-value: "" +`, ` +services: + test: + image: foo +networks: + network1: + ipam: + config: + - subnet: 172.28.0.0/16 + ip_range: 172.28.5.1/24 + gateway: 172.28.5.254 + aux_addresses: + host1: 172.28.1.5 + host2: 172.28.1.4 + host4: 172.28.1.10 + - subnet: 172.28.10.0/16 + ip_range: 172.28.10.1/24 + gateway: 172.28.10.254 + aux_addresses: + host1: 172.28.10.5 + host2: 172.28.10.4 + host3: 172.28.10.10 + options: + bar: foo + baz: "0" + labels: + com.example.description: "Financial transaction network" + com.example.department-new: "New" + com.example.label-with-empty-value: "" + network2: +`, ` +services: + test: + image: foo +networks: + network1: + ipam: + config: + - subnet: 172.28.0.0/16 + ip_range: 172.28.5.1/24 + gateway: 172.28.5.254 + aux_addresses: + host1: 172.28.1.5 + host2: 172.28.1.4 + host3: 172.28.1.7 + host4: 172.28.1.10 + - subnet: 172.28.10.0/16 + ip_range: 172.28.10.1/24 + gateway: 172.28.10.254 + aux_addresses: + host1: 172.28.10.5 + host2: 172.28.10.4 + host3: 172.28.10.10 + options: + foo: bar + bar: foo + baz: "0" + labels: + com.example.description: "Financial transaction network" + com.example.department: "Finance" + com.example.label-with-empty-value: "" + com.example.department-new: "New" + network2: +`) +} diff --git a/override/uncity.go b/override/uncity.go index c27ca0b7..d8f29b59 100644 --- a/override/uncity.go +++ b/override/uncity.go @@ -31,6 +31,8 @@ type indexer func(any, tree.Path) (string, error) var unique = map[tree.Path]indexer{} func init() { + unique["networks.*.labels"] = keyValueIndexer + unique["networks.*.ipam.options"] = keyValueIndexer unique["services.*.annotations"] = keyValueIndexer unique["services.*.build.args"] = keyValueIndexer unique["services.*.build.additional_contexts"] = keyValueIndexer