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
1=== modified file 'config/config.go'
2--- config/config.go 2014-02-21 16:17:28 +0000
3+++ config/config.go 2014-03-26 20:08:36 +0000
4@@ -31,10 +31,10 @@
5 "time"
6 )
7
8-func checkDestConfig(destConfig interface{}) (reflect.Value, error) {
9+func checkDestConfig(name string, destConfig interface{}) (reflect.Value, error) {
10 destValue := reflect.ValueOf(destConfig)
11 if destValue.Kind() != reflect.Ptr || destValue.Elem().Kind() != reflect.Struct {
12- return reflect.Value{}, errors.New("destConfig not *struct")
13+ return reflect.Value{}, fmt.Errorf("%s not *struct", name)
14 }
15 return destValue, nil
16 }
17@@ -44,6 +44,15 @@
18 dest interface{}
19 }
20
21+func (f destField) configName() string {
22+ fld := f.fld
23+ configName := strings.Split(fld.Tag.Get("json"), ",")[0]
24+ if configName == "" {
25+ configName = strings.ToLower(fld.Name[:1]) + fld.Name[1:]
26+ }
27+ return configName
28+}
29+
30 func traverseStruct(destStruct reflect.Value) <-chan destField {
31 ch := make(chan destField)
32 var traverse func(reflect.Value, chan<- destField)
33@@ -76,11 +85,7 @@
34 func fillDestConfig(destValue reflect.Value, p map[string]json.RawMessage) error {
35 destStruct := destValue.Elem()
36 for destField := range traverseStruct(destStruct) {
37- fld := destField.fld
38- configName := strings.Split(fld.Tag.Get("json"), ",")[0]
39- if configName == "" {
40- configName = strings.ToLower(fld.Name[:1]) + fld.Name[1:]
41- }
42+ configName := destField.configName()
43 raw, found := p[configName]
44 if !found { // assume all fields are mandatory for now
45 return fmt.Errorf("missing %s", configName)
46@@ -100,7 +105,7 @@
47 // fields in errors. Configuration fields in the JSON object are
48 // expected to start with lower case.
49 func ReadConfig(r io.Reader, destConfig interface{}) error {
50- destValue, err := checkDestConfig(destConfig)
51+ destValue, err := checkDestConfig("destConfig", destConfig)
52 if err != nil {
53 return err
54 }
55@@ -195,7 +200,7 @@
56
57 // ReadFiles reads configuration from a set of files. Uses ReadConfig internally.
58 func ReadFiles(destConfig interface{}, cfgFpaths ...string) error {
59- destValue, err := checkDestConfig(destConfig)
60+ destValue, err := checkDestConfig("destConfig", destConfig)
61 if err != nil {
62 return err
63 }
64@@ -221,3 +226,35 @@
65 }
66 return fillDestConfig(destValue, p1)
67 }
68+
69+// CompareConfigs compares the two given configuration structures. It returns a list of differing fields or nil if the config contents are the same.
70+func CompareConfig(config1, config2 interface{}) ([]string, error) {
71+ v1, err := checkDestConfig("config1", config1)
72+ if err != nil {
73+ return nil, err
74+ }
75+ v2, err := checkDestConfig("config2", config2)
76+ if err != nil {
77+ return nil, err
78+ }
79+ if v1.Type() != v2.Type() {
80+ return nil, errors.New("config1 and config2 don't have the same type")
81+ }
82+ fields1 := traverseStruct(v1.Elem())
83+ fields2 := traverseStruct(v2.Elem())
84+ diff := make([]string, 0)
85+ for {
86+ d1 := <-fields1
87+ d2 := <-fields2
88+ if d1.dest == nil {
89+ break
90+ }
91+ if !reflect.DeepEqual(d1.dest, d2.dest) {
92+ diff = append(diff, d1.configName())
93+ }
94+ }
95+ if len(diff) != 0 {
96+ return diff, nil
97+ }
98+ return nil, nil
99+}
100
101=== modified file 'config/config_test.go'
102--- config/config_test.go 2014-02-10 22:50:59 +0000
103+++ config/config_test.go 2014-03-26 20:08:36 +0000
104@@ -189,3 +189,44 @@
105 }
106 c.Check(a, DeepEquals, A{1, B{2}, 0})
107 }
108+
109+type testConfig2 struct {
110+ A int
111+ B string
112+ C []string `json:"c_list"`
113+ D ConfigTimeDuration
114+}
115+
116+func (s *configSuite) TestCompareConfig(c *C) {
117+ var cfg1 = testConfig2{
118+ A: 1,
119+ B: "xyz",
120+ C: []string{"a", "b"},
121+ D: ConfigTimeDuration{200 * time.Millisecond},
122+ }
123+ var cfg2 = testConfig2{
124+ A: 1,
125+ B: "xyz",
126+ C: []string{"a", "b"},
127+ D: ConfigTimeDuration{200 * time.Millisecond},
128+ }
129+ _, err := CompareConfig(cfg1, &cfg2)
130+ c.Check(err, ErrorMatches, `config1 not \*struct`)
131+ _, err = CompareConfig(&cfg1, cfg2)
132+ c.Check(err, ErrorMatches, `config2 not \*struct`)
133+ _, err = CompareConfig(&cfg1, &testConfig1{})
134+ c.Check(err, ErrorMatches, `config1 and config2 don't have the same type`)
135+
136+ res, err := CompareConfig(&cfg1, &cfg2)
137+ c.Assert(err, IsNil)
138+ c.Check(res, IsNil)
139+
140+ cfg1.B = "zyx"
141+ cfg2.C = []string{"a", "B"}
142+ cfg2.D = ConfigTimeDuration{205 * time.Millisecond}
143+
144+ res, err = CompareConfig(&cfg1, &cfg2)
145+ c.Assert(err, IsNil)
146+ c.Check(res, DeepEquals, []string{"b", "c_list", "d"})
147+
148+}

Subscribers

People subscribed via source and target branches