@cryptotaxi247 / kubo / commits / e1d14441a

fix(fsrepo): deep merge when setting config

Kyle Huntsman committed Mar 2, 2022 at 20:26 UTC e1d14441a090e893a145112dc2f26a6b20c9cf4b
4 files changed +165 -5
repo/common/common.go
+30
@@ -54,3 +54,33 @@ func MapSetKV(v map[string]interface{}, key string, value interface{}) error {
54 }
55 return nil
56 }
57 +
58 +// Merges the right map into the left map, recursively traversing child maps
59 +// until a non-map value is found
60 +func MapMergeDeep(left, right map[string]interface{}) map[string]interface{} {
61 + // We want to alter a copy of the map, not the original
62 + result := make(map[string]interface{})
63 + for k, v := range left {
64 + result[k] = v
65 + }
66 +
67 + for key, rightVal := range right {
68 + // If right value is a map
69 + if rightMap, ok := rightVal.(map[string]interface{}); ok {
70 + // If key is in left
71 + if leftVal, found := result[key]; found {
72 + // If left value is also a map
73 + if leftMap, ok := leftVal.(map[string]interface{}); ok {
74 + // Merge nested map
75 + result[key] = MapMergeDeep(leftMap, rightMap)
76 + continue
77 + }
78 + }
79 + }
80 +
81 + // Otherwise set new value to result
82 + result[key] = rightVal
83 + }
84 +
85 + return result
86 +}
repo/common/common_test.go new
+132
@@ -0,0 +1,132 @@
1 +package common
2 +
3 +import (
4 + "testing"
5 +
6 + "github.com/ipfs/go-ipfs/thirdparty/assert"
7 +)
8 +
9 +func TestMapMergeDeepReturnsNew(t *testing.T) {
10 + leftMap := make(map[string]interface{})
11 + leftMap["A"] = "Hello World"
12 +
13 + rightMap := make(map[string]interface{})
14 + rightMap["A"] = "Foo"
15 +
16 + MapMergeDeep(leftMap, rightMap)
17 +
18 + assert.True(leftMap["A"] == "Hello World", t, "MapMergeDeep should return a new map instance")
19 +}
20 +
21 +func TestMapMergeDeepNewKey(t *testing.T) {
22 + leftMap := make(map[string]interface{})
23 + leftMap["A"] = "Hello World"
24 + /*
25 + leftMap
26 + {
27 + A: "Hello World"
28 + }
29 + */
30 +
31 + rightMap := make(map[string]interface{})
32 + rightMap["B"] = "Bar"
33 + /*
34 + rightMap
35 + {
36 + B: "Bar"
37 + }
38 + */
39 +
40 + result := MapMergeDeep(leftMap, rightMap)
41 + /*
42 + expected
43 + {
44 + A: "Hello World"
45 + B: "Bar"
46 + }
47 + */
48 +
49 + assert.True(result["B"] == "Bar", t, "New keys in right map should exist in resulting map")
50 +}
51 +
52 +func TestMapMergeDeepRecursesOnMaps(t *testing.T) {
53 + leftMapA := make(map[string]interface{})
54 + leftMapA["B"] = "A value!"
55 + leftMapA["C"] = "Another value!"
56 +
57 + leftMap := make(map[string]interface{})
58 + leftMap["A"] = leftMapA
59 + /*
60 + leftMap
61 + {
62 + A: {
63 + B: "A value!"
64 + C: "Another value!"
65 + }
66 + }
67 + */
68 +
69 + rightMapA := make(map[string]interface{})
70 + rightMapA["C"] = "A different value!"
71 +
72 + rightMap := make(map[string]interface{})
73 + rightMap["A"] = rightMapA
74 + /*
75 + rightMap
76 + {
77 + A: {
78 + C: "A different value!"
79 + }
80 + }
81 + */
82 +
83 + result := MapMergeDeep(leftMap, rightMap)
84 + /*
85 + expected
86 + {
87 + A: {
88 + B: "A value!"
89 + C: "A different value!"
90 + }
91 + }
92 + */
93 +
94 + resultA := result["A"].(map[string]interface{})
95 + assert.True(resultA["B"] == "A value!", t, "Unaltered values should not change")
96 + assert.True(resultA["C"] == "A different value!", t, "Nested values should be altered")
97 +}
98 +
99 +func TestMapMergeDeepRightNotAMap(t *testing.T) {
100 + leftMapA := make(map[string]interface{})
101 + leftMapA["B"] = "A value!"
102 +
103 + leftMap := make(map[string]interface{})
104 + leftMap["A"] = leftMapA
105 + /*
106 + origMap
107 + {
108 + A: {
109 + B: "A value!"
110 + }
111 + }
112 + */
113 +
114 + rightMap := make(map[string]interface{})
115 + rightMap["A"] = "Not a map!"
116 + /*
117 + newMap
118 + {
119 + A: "Not a map!"
120 + }
121 + */
122 +
123 + result := MapMergeDeep(leftMap, rightMap)
124 + /*
125 + expected
126 + {
127 + A: "Not a map!"
128 + }
129 + */
130 +
131 + assert.True(result["A"] == "Not a map!", t, "Right values that are not a map should be set on the result")
132 +}
repo/fsrepo/fsrepo.go
+2 -4
@@ -559,10 +559,8 @@ func (r *FSRepo) setConfigUnsynced(updated *config.Config) error {
559 if err != nil {
560 return err
561 }
562 - for k, v := range m {
563 - mapconf[k] = v
564 - }
565 - if err := serialize.WriteConfigFile(configFilename, mapconf); err != nil {
562 + mergedMap := common.MapMergeDeep(mapconf, m)
563 + if err := serialize.WriteConfigFile(configFilename, mergedMap); err != nil {
564 return err
565 }
566 // Do not use `*r.config = ...`. This will modify the *shared* config
test/sharness/t0250-files-api.sh
+1 -1
@@ -876,7 +876,7 @@ test_expect_success "set up automatic sharding/unsharding data" '
876 '
877
878 # TODO: This does not need to report an error https://github.com/ipfs/go-ipfs/issues/8088
879 -test_expect_failure "reset automatic sharding" '
879 +test_expect_success "reset automatic sharding" '
880 ipfs config --json Internal.UnixFSShardingSizeThreshold null
881 '
882