blob: 0546bd35868eb99f49893865c564c323095acf6e [file] [log] [blame]
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package model
17
18import (
19 "github.com/opencord/voltha-go/common/log"
20 "reflect"
21)
22
23func revisionsAreEqual(a, b []Revision) bool {
24 // If one is nil, the other must also be nil.
25 if (a == nil) != (b == nil) {
26 return false
27 }
28
29 if len(a) != len(b) {
30 return false
31 }
32
33 for i := range a {
34 if a[i] != b[i] {
35 return false
36 }
37 }
38
39 return true
40}
41
42type changeAnalysis struct {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040043 KeyMap1 map[string]int
44 KeyMap2 map[string]int
45 AddedKeys map[string]struct{}
46 RemovedKeys map[string]struct{}
47 ChangedKeys map[string]struct{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -040048}
49
50func newChangeAnalysis(lst1, lst2 []Revision, keyName string) *changeAnalysis {
51 changes := &changeAnalysis{}
52
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040053 changes.KeyMap1 = make(map[string]int)
54 changes.KeyMap2 = make(map[string]int)
Stephane Barbarieec0919b2018-09-05 14:14:29 -040055
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040056 changes.AddedKeys = make(map[string]struct{})
57 changes.RemovedKeys = make(map[string]struct{})
58 changes.ChangedKeys = make(map[string]struct{})
Stephane Barbarieec0919b2018-09-05 14:14:29 -040059
60 for i, rev := range lst1 {
61 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040062 changes.KeyMap1[v.String()] = i
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063 }
64 for i, rev := range lst2 {
65 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040066 changes.KeyMap2[v.String()] = i
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 }
68 for v, _ := range changes.KeyMap2 {
69 if _, ok := changes.KeyMap1[v]; !ok {
70 changes.AddedKeys[v] = struct{}{}
71 }
72 }
73 for v, _ := range changes.KeyMap1 {
74 if _, ok := changes.KeyMap2[v]; !ok {
75 changes.RemovedKeys[v] = struct{}{}
76 }
77 }
78 for v, _ := range changes.KeyMap1 {
Stephane Barbariee16186c2018-09-11 10:46:34 -040079 if _, ok := changes.KeyMap2[v]; ok && lst1[changes.KeyMap1[v]].GetHash() != lst2[changes.KeyMap2[v]].GetHash() {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040080 changes.ChangedKeys[v] = struct{}{}
81 }
82 }
83
84 return changes
85}
86
87func Merge3Way(
88 forkRev, srcRev, dstRev Revision,
89 mergeChildFunc func(Revision) Revision,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040090 dryRun bool) (rev Revision, changes []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091
92 var configChanged bool
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040093 var revsToDiscard []Revision
Stephane Barbarieec0919b2018-09-05 14:14:29 -040094
95 if dstRev.GetConfig() == forkRev.GetConfig() {
96 configChanged = dstRev.GetConfig() != srcRev.GetConfig()
97 } else {
98 if dstRev.GetConfig().Hash != srcRev.GetConfig().Hash {
99 log.Error("config-collision")
100 }
101 configChanged = true
102 }
103
Stephane Barbariee16186c2018-09-11 10:46:34 -0400104 newChildren := reflect.ValueOf(dstRev.GetChildren()).Interface().(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400105 childrenFields := ChildrenFields(forkRev.GetData())
106
107 for fieldName, field := range childrenFields {
108 forkList := forkRev.GetChildren()[fieldName]
109 srcList := srcRev.GetChildren()[fieldName]
110 dstList := dstRev.GetChildren()[fieldName]
111
Stephane Barbariee16186c2018-09-11 10:46:34 -0400112 if revisionsAreEqual(dstList, srcList) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400113 for _, rev := range srcList {
114 mergeChildFunc(rev)
115 }
116 continue
117 }
118
119 if field.Key == "" {
120 if revisionsAreEqual(dstList, forkList) {
121 if !revisionsAreEqual(srcList, forkList) {
122 log.Error("we should not be here")
123 } else {
124 for _, rev := range srcList {
125 newChildren[fieldName] = append(newChildren[fieldName], mergeChildFunc(rev))
126 }
127 if field.IsContainer {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400128 changes = append(
129 changes, ChangeTuple{POST_LISTCHANGE,
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400130 NewOperationContext("", nil, fieldName, ""), nil},
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400131 )
132 }
133 }
134 } else {
135 if !revisionsAreEqual(srcList, forkList) {
136 log.Error("cannot merge - single child node or un-keyed children list has changed")
137 }
138 }
139 } else {
140 if revisionsAreEqual(dstList, forkList) {
141 src := newChangeAnalysis(forkList, srcList, field.Key)
142
Stephane Barbariee16186c2018-09-11 10:46:34 -0400143 newList := reflect.ValueOf(srcList).Interface().([]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400144
145 for key, _ := range src.AddedKeys {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400146 idx := src.KeyMap2[key]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400147 newRev := mergeChildFunc(newList[idx])
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400148
Stephane Barbariee16186c2018-09-11 10:46:34 -0400149 // FIXME: newRev may come back as nil... exclude those entries for now
150 if newRev != nil {
151 newList[idx] = newRev
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400152 changes = append(changes, ChangeTuple{POST_ADD, newList[idx].GetData(), newRev.GetData()})
Stephane Barbariee16186c2018-09-11 10:46:34 -0400153 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400154 }
155 for key, _ := range src.RemovedKeys {
156 oldRev := forkList[src.KeyMap1[key]]
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400157 revsToDiscard = append(revsToDiscard, oldRev)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400158 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData(), nil})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400159 }
160 for key, _ := range src.ChangedKeys {
161 idx := src.KeyMap2[key]
162 newRev := mergeChildFunc(newList[idx])
Stephane Barbariee16186c2018-09-11 10:46:34 -0400163
164 // FIXME: newRev may come back as nil... exclude those entries for now
165 if newRev != nil {
166 newList[idx] = newRev
167 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400168 }
169
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400170 if !dryRun {
171 newChildren[fieldName] = newList
172 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400173 } else {
174 src := newChangeAnalysis(forkList, srcList, field.Key)
175 dst := newChangeAnalysis(forkList, dstList, field.Key)
176
Stephane Barbariee16186c2018-09-11 10:46:34 -0400177 newList := reflect.ValueOf(dstList).Interface().([]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400178
179 for key, _ := range src.AddedKeys {
180 if _, exists := dst.AddedKeys[key]; exists {
181 childDstRev := dstList[dst.KeyMap2[key]]
182 childSrcRev := srcList[src.KeyMap2[key]]
183 if childDstRev.GetHash() == childSrcRev.GetHash() {
184 mergeChildFunc(childDstRev)
185 } else {
186 log.Error("conflict error - revision has been added is different")
187 }
188 } else {
189 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
190 newList = append(newList, newRev)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400191 changes = append(changes, ChangeTuple{POST_ADD, srcList[src.KeyMap2[key]], newRev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400192 }
193 }
194 for key, _ := range src.ChangedKeys {
195 if _, removed := dst.RemovedKeys[key]; removed {
196 log.Error("conflict error - revision has been removed")
197 } else if _, changed := dst.ChangedKeys[key]; changed {
198 childDstRev := dstList[dst.KeyMap2[key]]
199 childSrcRev := srcList[src.KeyMap2[key]]
200 if childDstRev.GetHash() == childSrcRev.GetHash() {
201 mergeChildFunc(childSrcRev)
202 } else if childDstRev.GetConfig().Hash != childSrcRev.GetConfig().Hash {
203 log.Error("conflict error - revision has been changed and is different")
204 } else {
205 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
206 newList[dst.KeyMap2[key]] = newRev
207 }
208 } else {
209 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
210 newList[dst.KeyMap2[key]] = newRev
211 }
212 }
213
214 // TODO: how do i sort this map in reverse order?
215 for key, _ := range src.RemovedKeys {
216 if _, changed := dst.ChangedKeys[key]; changed {
217 log.Error("conflict error - revision has changed")
218 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400219 if _, removed := dst.RemovedKeys[key]; !removed {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400220 dstIdx := dst.KeyMap2[key]
221 oldRev := newList[dstIdx]
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400222 revsToDiscard = append(revsToDiscard, oldRev)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400223
224 copy(newList[dstIdx:], newList[dstIdx+1:])
225 newList[len(newList)-1] = nil
226 newList = newList[:len(newList)-1]
227
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400228 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData(), nil})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400229 }
230 }
231
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400232 if !dryRun {
233 newChildren[fieldName] = newList
234 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400235 }
236 }
237 }
238
239 if !dryRun {
240 if configChanged {
241 rev = srcRev
242 } else {
243 rev = dstRev
244 }
245
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400246 for _, discarded := range revsToDiscard {
247 discarded.Drop("", true)
248 }
249
250 dstRev.GetBranch().Latest.Drop("", configChanged)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400251 rev = rev.UpdateAllChildren(newChildren, dstRev.GetBranch())
252
253 if configChanged {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400254 changes = append(changes, ChangeTuple{POST_UPDATE, dstRev.GetBranch().Latest.GetData(), rev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400255 }
256 return rev, changes
Stephane Barbariee16186c2018-09-11 10:46:34 -0400257 } else {
258 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400259
Stephane Barbariee16186c2018-09-11 10:46:34 -0400260 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400261}