Merge lp:~pedronis/ubuntu-push/compare-config into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Merged
Approved by: John Lenton
Approved revision: 90
Merged at revision: 92
Proposed branch: lp:~pedronis/ubuntu-push/compare-config
Merge into: lp:ubuntu-push
Diff against target: 148 lines (+87/-9)
2 files modified
config/config.go (+46/-9)
config/config_test.go (+41/-0)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/compare-config
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+212943@code.launchpad.net

Commit message

introduce a way to compare configs

Description of the change

introduce a way to compare configs

To post a comment you must log in.
Revision history for this message
John Lenton (chipaca) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config/config.go'
--- config/config.go 2014-02-21 16:17:28 +0000
+++ config/config.go 2014-03-26 20:08:36 +0000
@@ -31,10 +31,10 @@
31 "time"31 "time"
32)32)
3333
34func checkDestConfig(destConfig interface{}) (reflect.Value, error) {34func checkDestConfig(name string, destConfig interface{}) (reflect.Value, error) {
35 destValue := reflect.ValueOf(destConfig)35 destValue := reflect.ValueOf(destConfig)
36 if destValue.Kind() != reflect.Ptr || destValue.Elem().Kind() != reflect.Struct {36 if destValue.Kind() != reflect.Ptr || destValue.Elem().Kind() != reflect.Struct {
37 return reflect.Value{}, errors.New("destConfig not *struct")37 return reflect.Value{}, fmt.Errorf("%s not *struct", name)
38 }38 }
39 return destValue, nil39 return destValue, nil
40}40}
@@ -44,6 +44,15 @@
44 dest interface{}44 dest interface{}
45}45}
4646
47func (f destField) configName() string {
48 fld := f.fld
49 configName := strings.Split(fld.Tag.Get("json"), ",")[0]
50 if configName == "" {
51 configName = strings.ToLower(fld.Name[:1]) + fld.Name[1:]
52 }
53 return configName
54}
55
47func traverseStruct(destStruct reflect.Value) <-chan destField {56func traverseStruct(destStruct reflect.Value) <-chan destField {
48 ch := make(chan destField)57 ch := make(chan destField)
49 var traverse func(reflect.Value, chan<- destField)58 var traverse func(reflect.Value, chan<- destField)
@@ -76,11 +85,7 @@
76func fillDestConfig(destValue reflect.Value, p map[string]json.RawMessage) error {85func fillDestConfig(destValue reflect.Value, p map[string]json.RawMessage) error {
77 destStruct := destValue.Elem()86 destStruct := destValue.Elem()
78 for destField := range traverseStruct(destStruct) {87 for destField := range traverseStruct(destStruct) {
79 fld := destField.fld88 configName := destField.configName()
80 configName := strings.Split(fld.Tag.Get("json"), ",")[0]
81 if configName == "" {
82 configName = strings.ToLower(fld.Name[:1]) + fld.Name[1:]
83 }
84 raw, found := p[configName]89 raw, found := p[configName]
85 if !found { // assume all fields are mandatory for now90 if !found { // assume all fields are mandatory for now
86 return fmt.Errorf("missing %s", configName)91 return fmt.Errorf("missing %s", configName)
@@ -100,7 +105,7 @@
100// fields in errors. Configuration fields in the JSON object are105// fields in errors. Configuration fields in the JSON object are
101// expected to start with lower case.106// expected to start with lower case.
102func ReadConfig(r io.Reader, destConfig interface{}) error {107func ReadConfig(r io.Reader, destConfig interface{}) error {
103 destValue, err := checkDestConfig(destConfig)108 destValue, err := checkDestConfig("destConfig", destConfig)
104 if err != nil {109 if err != nil {
105 return err110 return err
106 }111 }
@@ -195,7 +200,7 @@
195200
196// ReadFiles reads configuration from a set of files. Uses ReadConfig internally.201// ReadFiles reads configuration from a set of files. Uses ReadConfig internally.
197func ReadFiles(destConfig interface{}, cfgFpaths ...string) error {202func ReadFiles(destConfig interface{}, cfgFpaths ...string) error {
198 destValue, err := checkDestConfig(destConfig)203 destValue, err := checkDestConfig("destConfig", destConfig)
199 if err != nil {204 if err != nil {
200 return err205 return err
201 }206 }
@@ -221,3 +226,35 @@
221 }226 }
222 return fillDestConfig(destValue, p1)227 return fillDestConfig(destValue, p1)
223}228}
229
230// CompareConfigs compares the two given configuration structures. It returns a list of differing fields or nil if the config contents are the same.
231func CompareConfig(config1, config2 interface{}) ([]string, error) {
232 v1, err := checkDestConfig("config1", config1)
233 if err != nil {
234 return nil, err
235 }
236 v2, err := checkDestConfig("config2", config2)
237 if err != nil {
238 return nil, err
239 }
240 if v1.Type() != v2.Type() {
241 return nil, errors.New("config1 and config2 don't have the same type")
242 }
243 fields1 := traverseStruct(v1.Elem())
244 fields2 := traverseStruct(v2.Elem())
245 diff := make([]string, 0)
246 for {
247 d1 := <-fields1
248 d2 := <-fields2
249 if d1.dest == nil {
250 break
251 }
252 if !reflect.DeepEqual(d1.dest, d2.dest) {
253 diff = append(diff, d1.configName())
254 }
255 }
256 if len(diff) != 0 {
257 return diff, nil
258 }
259 return nil, nil
260}
224261
=== modified file 'config/config_test.go'
--- config/config_test.go 2014-02-10 22:50:59 +0000
+++ config/config_test.go 2014-03-26 20:08:36 +0000
@@ -189,3 +189,44 @@
189 }189 }
190 c.Check(a, DeepEquals, A{1, B{2}, 0})190 c.Check(a, DeepEquals, A{1, B{2}, 0})
191}191}
192
193type testConfig2 struct {
194 A int
195 B string
196 C []string `json:"c_list"`
197 D ConfigTimeDuration
198}
199
200func (s *configSuite) TestCompareConfig(c *C) {
201 var cfg1 = testConfig2{
202 A: 1,
203 B: "xyz",
204 C: []string{"a", "b"},
205 D: ConfigTimeDuration{200 * time.Millisecond},
206 }
207 var cfg2 = testConfig2{
208 A: 1,
209 B: "xyz",
210 C: []string{"a", "b"},
211 D: ConfigTimeDuration{200 * time.Millisecond},
212 }
213 _, err := CompareConfig(cfg1, &cfg2)
214 c.Check(err, ErrorMatches, `config1 not \*struct`)
215 _, err = CompareConfig(&cfg1, cfg2)
216 c.Check(err, ErrorMatches, `config2 not \*struct`)
217 _, err = CompareConfig(&cfg1, &testConfig1{})
218 c.Check(err, ErrorMatches, `config1 and config2 don't have the same type`)
219
220 res, err := CompareConfig(&cfg1, &cfg2)
221 c.Assert(err, IsNil)
222 c.Check(res, IsNil)
223
224 cfg1.B = "zyx"
225 cfg2.C = []string{"a", "B"}
226 cfg2.D = ConfigTimeDuration{205 * time.Millisecond}
227
228 res, err = CompareConfig(&cfg1, &cfg2)
229 c.Assert(err, IsNil)
230 c.Check(res, DeepEquals, []string{"b", "c_list", "d"})
231
232}

Subscribers

People subscribed via source and target branches