Browse code

Vendor merge structure tool.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2016/01/06 08:34:22
Showing 8 changed files
... ...
@@ -24,6 +24,7 @@ clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
24 24
 clone git github.com/docker/go-connections v0.1.2
25 25
 clone git github.com/docker/engine-api v0.2.1
26 26
 clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
27
+clone git github.com/imdario/mergo 0.2.1
27 28
 
28 29
 #get libnetwork packages
29 30
 clone git github.com/docker/libnetwork v0.5.4
30 31
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+language: go
1
+install: go get -t
0 2
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+Copyright (c) 2013 Dario Castañé. All rights reserved.
1
+Copyright (c) 2012 The Go Authors. All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are
5
+met:
6
+
7
+   * Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+   * Redistributions in binary form must reproduce the above
10
+copyright notice, this list of conditions and the following disclaimer
11
+in the documentation and/or other materials provided with the
12
+distribution.
13
+   * Neither the name of Google Inc. nor the names of its
14
+contributors may be used to endorse or promote products derived from
15
+this software without specific prior written permission.
16
+
17
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 28
new file mode 100644
... ...
@@ -0,0 +1,122 @@
0
+# Mergo
1
+
2
+A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
3
+
4
+Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region Marche.
5
+
6
+![Mergo dall'alto](http://www.comune.mergo.an.it/Siti/Mergo/Immagini/Foto/mergo_dall_alto.jpg)
7
+
8
+## Status
9
+
10
+It is ready for production use. It works fine after extensive use in the wild.
11
+
12
+[![Build Status][1]][2]
13
+[![GoDoc](https://godoc.org/github.com/imdario/mergo?status.svg)](https://godoc.org/github.com/imdario/mergo)
14
+
15
+[1]: https://travis-ci.org/imdario/mergo.png
16
+[2]: https://travis-ci.org/imdario/mergo
17
+
18
+### Important note
19
+
20
+Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
21
+
22
+If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
23
+
24
+### Mergo in the wild
25
+
26
+- [imdario/zas](https://github.com/imdario/zas)
27
+- [GoogleCloudPlatform/kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
28
+- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
29
+- [EagerIO/Stout](https://github.com/EagerIO/Stout)
30
+- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
31
+- [russross/canvasassignments](https://github.com/russross/canvasassignments)
32
+- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api)
33
+- [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
34
+- [divshot/gitling](https://github.com/divshot/gitling)
35
+- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
36
+- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
37
+- [elwinar/rambler](https://github.com/elwinar/rambler)
38
+- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
39
+- [jfbus/impressionist](https://github.com/jfbus/impressionist)
40
+- [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
41
+- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
42
+- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
43
+- [thoas/picfit](https://github.com/thoas/picfit)
44
+- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
45
+- [jnuthong/item_search](https://github.com/jnuthong/item_search)
46
+
47
+## Installation
48
+
49
+    go get github.com/imdario/mergo
50
+
51
+    // use in your .go code
52
+    import (
53
+        "github.com/imdario/mergo"
54
+    )
55
+
56
+## Usage
57
+
58
+You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
59
+
60
+    if err := mergo.Merge(&dst, src); err != nil {
61
+        // ...
62
+    }
63
+
64
+Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field.
65
+
66
+    if err := mergo.Map(&dst, srcMap); err != nil {
67
+        // ...
68
+    }
69
+
70
+Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values.
71
+
72
+More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo).
73
+
74
+### Nice example
75
+
76
+```go
77
+package main
78
+
79
+import (
80
+	"fmt"
81
+	"github.com/imdario/mergo"
82
+)
83
+
84
+type Foo struct {
85
+	A string
86
+	B int64
87
+}
88
+
89
+func main() {
90
+	src := Foo{
91
+		A: "one",
92
+	}
93
+
94
+	dest := Foo{
95
+		A: "two",
96
+		B: 2,
97
+	}
98
+
99
+	mergo.Merge(&dest, src)
100
+
101
+	fmt.Println(dest)
102
+	// Will print
103
+	// {two 2}
104
+}
105
+```
106
+
107
+Note: if test are failing due missing package, please execute:
108
+
109
+    go get gopkg.in/yaml.v1
110
+
111
+## Contact me
112
+
113
+If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario)
114
+
115
+## About
116
+
117
+Written by [Dario Castañé](http://dario.im).
118
+
119
+## License
120
+
121
+[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
0 122
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+// Copyright 2013 Dario Castañé. All rights reserved.
1
+// Copyright 2009 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+/*
6
+Package mergo merges same-type structs and maps by setting default values in zero-value fields.
7
+
8
+Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
9
+
10
+Usage
11
+
12
+From my own work-in-progress project:
13
+
14
+	type networkConfig struct {
15
+		Protocol string
16
+		Address string
17
+		ServerType string `json: "server_type"`
18
+		Port uint16
19
+	}
20
+
21
+	type FssnConfig struct {
22
+		Network networkConfig
23
+	}
24
+
25
+	var fssnDefault = FssnConfig {
26
+		networkConfig {
27
+			"tcp",
28
+			"127.0.0.1",
29
+			"http",
30
+			31560,
31
+		},
32
+	}
33
+
34
+	// Inside a function [...]
35
+
36
+	if err := mergo.Merge(&config, fssnDefault); err != nil {
37
+		log.Fatal(err)
38
+	}
39
+
40
+	// More code [...]
41
+
42
+*/
43
+package mergo
0 44
new file mode 100644
... ...
@@ -0,0 +1,154 @@
0
+// Copyright 2014 Dario Castañé. All rights reserved.
1
+// Copyright 2009 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// Based on src/pkg/reflect/deepequal.go from official
6
+// golang's stdlib.
7
+
8
+package mergo
9
+
10
+import (
11
+	"fmt"
12
+	"reflect"
13
+	"unicode"
14
+	"unicode/utf8"
15
+)
16
+
17
+func changeInitialCase(s string, mapper func(rune) rune) string {
18
+	if s == "" {
19
+		return s
20
+	}
21
+	r, n := utf8.DecodeRuneInString(s)
22
+	return string(mapper(r)) + s[n:]
23
+}
24
+
25
+func isExported(field reflect.StructField) bool {
26
+	r, _ := utf8.DecodeRuneInString(field.Name)
27
+	return r >= 'A' && r <= 'Z'
28
+}
29
+
30
+// Traverses recursively both values, assigning src's fields values to dst.
31
+// The map argument tracks comparisons that have already been seen, which allows
32
+// short circuiting on recursive types.
33
+func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
34
+	if dst.CanAddr() {
35
+		addr := dst.UnsafeAddr()
36
+		h := 17 * addr
37
+		seen := visited[h]
38
+		typ := dst.Type()
39
+		for p := seen; p != nil; p = p.next {
40
+			if p.ptr == addr && p.typ == typ {
41
+				return nil
42
+			}
43
+		}
44
+		// Remember, remember...
45
+		visited[h] = &visit{addr, typ, seen}
46
+	}
47
+	zeroValue := reflect.Value{}
48
+	switch dst.Kind() {
49
+	case reflect.Map:
50
+		dstMap := dst.Interface().(map[string]interface{})
51
+		for i, n := 0, src.NumField(); i < n; i++ {
52
+			srcType := src.Type()
53
+			field := srcType.Field(i)
54
+			if !isExported(field) {
55
+				continue
56
+			}
57
+			fieldName := field.Name
58
+			fieldName = changeInitialCase(fieldName, unicode.ToLower)
59
+			if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) {
60
+				dstMap[fieldName] = src.Field(i).Interface()
61
+			}
62
+		}
63
+	case reflect.Struct:
64
+		srcMap := src.Interface().(map[string]interface{})
65
+		for key := range srcMap {
66
+			srcValue := srcMap[key]
67
+			fieldName := changeInitialCase(key, unicode.ToUpper)
68
+			dstElement := dst.FieldByName(fieldName)
69
+			if dstElement == zeroValue {
70
+				// We discard it because the field doesn't exist.
71
+				continue
72
+			}
73
+			srcElement := reflect.ValueOf(srcValue)
74
+			dstKind := dstElement.Kind()
75
+			srcKind := srcElement.Kind()
76
+			if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
77
+				srcElement = srcElement.Elem()
78
+				srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
79
+			} else if dstKind == reflect.Ptr {
80
+				// Can this work? I guess it can't.
81
+				if srcKind != reflect.Ptr && srcElement.CanAddr() {
82
+					srcPtr := srcElement.Addr()
83
+					srcElement = reflect.ValueOf(srcPtr)
84
+					srcKind = reflect.Ptr
85
+				}
86
+			}
87
+			if !srcElement.IsValid() {
88
+				continue
89
+			}
90
+			if srcKind == dstKind {
91
+				if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
92
+					return
93
+				}
94
+			} else {
95
+				if srcKind == reflect.Map {
96
+					if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
97
+						return
98
+					}
99
+				} else {
100
+					return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
101
+				}
102
+			}
103
+		}
104
+	}
105
+	return
106
+}
107
+
108
+// Map sets fields' values in dst from src.
109
+// src can be a map with string keys or a struct. dst must be the opposite:
110
+// if src is a map, dst must be a valid pointer to struct. If src is a struct,
111
+// dst must be map[string]interface{}.
112
+// It won't merge unexported (private) fields and will do recursively
113
+// any exported field.
114
+// If dst is a map, keys will be src fields' names in lower camel case.
115
+// Missing key in src that doesn't match a field in dst will be skipped. This
116
+// doesn't apply if dst is a map.
117
+// This is separated method from Merge because it is cleaner and it keeps sane
118
+// semantics: merging equal types, mapping different (restricted) types.
119
+func Map(dst, src interface{}) error {
120
+	return _map(dst, src, false)
121
+}
122
+
123
+func MapWithOverwrite(dst, src interface{}) error {
124
+	return _map(dst, src, true)
125
+}
126
+
127
+func _map(dst, src interface{}, overwrite bool) error {
128
+	var (
129
+		vDst, vSrc reflect.Value
130
+		err        error
131
+	)
132
+	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
133
+		return err
134
+	}
135
+	// To be friction-less, we redirect equal-type arguments
136
+	// to deepMerge. Only because arguments can be anything.
137
+	if vSrc.Kind() == vDst.Kind() {
138
+		return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
139
+	}
140
+	switch vSrc.Kind() {
141
+	case reflect.Struct:
142
+		if vDst.Kind() != reflect.Map {
143
+			return ErrExpectedMapAsDestination
144
+		}
145
+	case reflect.Map:
146
+		if vDst.Kind() != reflect.Struct {
147
+			return ErrExpectedStructAsDestination
148
+		}
149
+	default:
150
+		return ErrNotSupported
151
+	}
152
+	return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
153
+}
0 154
new file mode 100644
... ...
@@ -0,0 +1,120 @@
0
+// Copyright 2013 Dario Castañé. All rights reserved.
1
+// Copyright 2009 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// Based on src/pkg/reflect/deepequal.go from official
6
+// golang's stdlib.
7
+
8
+package mergo
9
+
10
+import (
11
+	"reflect"
12
+)
13
+
14
+// Traverses recursively both values, assigning src's fields values to dst.
15
+// The map argument tracks comparisons that have already been seen, which allows
16
+// short circuiting on recursive types.
17
+func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
18
+	if !src.IsValid() {
19
+		return
20
+	}
21
+	if dst.CanAddr() {
22
+		addr := dst.UnsafeAddr()
23
+		h := 17 * addr
24
+		seen := visited[h]
25
+		typ := dst.Type()
26
+		for p := seen; p != nil; p = p.next {
27
+			if p.ptr == addr && p.typ == typ {
28
+				return nil
29
+			}
30
+		}
31
+		// Remember, remember...
32
+		visited[h] = &visit{addr, typ, seen}
33
+	}
34
+	switch dst.Kind() {
35
+	case reflect.Struct:
36
+		for i, n := 0, dst.NumField(); i < n; i++ {
37
+			if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
38
+				return
39
+			}
40
+		}
41
+	case reflect.Map:
42
+		for _, key := range src.MapKeys() {
43
+			srcElement := src.MapIndex(key)
44
+			if !srcElement.IsValid() {
45
+				continue
46
+			}
47
+			dstElement := dst.MapIndex(key)
48
+			switch srcElement.Kind() {
49
+			case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
50
+				if srcElement.IsNil() {
51
+					continue
52
+				}
53
+				fallthrough
54
+			default:
55
+				switch reflect.TypeOf(srcElement.Interface()).Kind() {
56
+				case reflect.Struct:
57
+					fallthrough
58
+				case reflect.Ptr:
59
+					fallthrough
60
+				case reflect.Map:
61
+					if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
62
+						return
63
+					}
64
+				}
65
+			}
66
+			if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
67
+				if dst.IsNil() {
68
+					dst.Set(reflect.MakeMap(dst.Type()))
69
+				}
70
+				dst.SetMapIndex(key, srcElement)
71
+			}
72
+		}
73
+	case reflect.Ptr:
74
+		fallthrough
75
+	case reflect.Interface:
76
+		if src.IsNil() {
77
+			break
78
+		} else if dst.IsNil() {
79
+			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
80
+				dst.Set(src)
81
+			}
82
+		} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
83
+			return
84
+		}
85
+	default:
86
+		if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
87
+			dst.Set(src)
88
+		}
89
+	}
90
+	return
91
+}
92
+
93
+// Merge sets fields' values in dst from src if they have a zero
94
+// value of their type.
95
+// dst and src must be valid same-type structs and dst must be
96
+// a pointer to struct.
97
+// It won't merge unexported (private) fields and will do recursively
98
+// any exported field.
99
+func Merge(dst, src interface{}) error {
100
+	return merge(dst, src, false)
101
+}
102
+
103
+func MergeWithOverwrite(dst, src interface{}) error {
104
+	return merge(dst, src, true)
105
+}
106
+
107
+func merge(dst, src interface{}, overwrite bool) error {
108
+	var (
109
+		vDst, vSrc reflect.Value
110
+		err        error
111
+	)
112
+	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
113
+		return err
114
+	}
115
+	if vDst.Type() != vSrc.Type() {
116
+		return ErrDifferentArgumentsTypes
117
+	}
118
+	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
119
+}
0 120
new file mode 100644
... ...
@@ -0,0 +1,90 @@
0
+// Copyright 2013 Dario Castañé. All rights reserved.
1
+// Copyright 2009 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// Based on src/pkg/reflect/deepequal.go from official
6
+// golang's stdlib.
7
+
8
+package mergo
9
+
10
+import (
11
+	"errors"
12
+	"reflect"
13
+)
14
+
15
+// Errors reported by Mergo when it finds invalid arguments.
16
+var (
17
+	ErrNilArguments                = errors.New("src and dst must not be nil")
18
+	ErrDifferentArgumentsTypes     = errors.New("src and dst must be of same type")
19
+	ErrNotSupported                = errors.New("only structs and maps are supported")
20
+	ErrExpectedMapAsDestination    = errors.New("dst was expected to be a map")
21
+	ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
22
+)
23
+
24
+// During deepMerge, must keep track of checks that are
25
+// in progress.  The comparison algorithm assumes that all
26
+// checks in progress are true when it reencounters them.
27
+// Visited are stored in a map indexed by 17 * a1 + a2;
28
+type visit struct {
29
+	ptr  uintptr
30
+	typ  reflect.Type
31
+	next *visit
32
+}
33
+
34
+// From src/pkg/encoding/json.
35
+func isEmptyValue(v reflect.Value) bool {
36
+	switch v.Kind() {
37
+	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
38
+		return v.Len() == 0
39
+	case reflect.Bool:
40
+		return !v.Bool()
41
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
42
+		return v.Int() == 0
43
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
44
+		return v.Uint() == 0
45
+	case reflect.Float32, reflect.Float64:
46
+		return v.Float() == 0
47
+	case reflect.Interface, reflect.Ptr:
48
+		return v.IsNil()
49
+	}
50
+	return false
51
+}
52
+
53
+func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
54
+	if dst == nil || src == nil {
55
+		err = ErrNilArguments
56
+		return
57
+	}
58
+	vDst = reflect.ValueOf(dst).Elem()
59
+	if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map {
60
+		err = ErrNotSupported
61
+		return
62
+	}
63
+	vSrc = reflect.ValueOf(src)
64
+	// We check if vSrc is a pointer to dereference it.
65
+	if vSrc.Kind() == reflect.Ptr {
66
+		vSrc = vSrc.Elem()
67
+	}
68
+	return
69
+}
70
+
71
+// Traverses recursively both values, assigning src's fields values to dst.
72
+// The map argument tracks comparisons that have already been seen, which allows
73
+// short circuiting on recursive types.
74
+func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) {
75
+	if dst.CanAddr() {
76
+		addr := dst.UnsafeAddr()
77
+		h := 17 * addr
78
+		seen := visited[h]
79
+		typ := dst.Type()
80
+		for p := seen; p != nil; p = p.next {
81
+			if p.ptr == addr && p.typ == typ {
82
+				return nil
83
+			}
84
+		}
85
+		// Remember, remember...
86
+		visited[h] = &visit{addr, typ, seen}
87
+	}
88
+	return // TODO refactor
89
+}