...
1 package conditions
2
3 import (
4 "reflect"
5
6 "github.com/google/go-cmp/cmp"
7 "github.com/pkg/errors"
8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9 )
10
11
12 type Patch []PatchOperation
13
14
15 type PatchOperation struct {
16 Before *metav1.Condition
17 After *metav1.Condition
18 Op PatchOperationType
19 }
20
21
22 type PatchOperationType string
23
24 const (
25
26 AddConditionPatch PatchOperationType = "Add"
27
28
29 ChangeConditionPatch PatchOperationType = "Change"
30
31
32 RemoveConditionPatch PatchOperationType = "Remove"
33 )
34
35
36 func NewPatch(before Getter, after Getter) Patch {
37 var patch Patch
38
39
40 targetConditions := after.GetConditions()
41 for i := range targetConditions {
42 targetCondition := targetConditions[i]
43 currentCondition := Get(before, targetCondition.Type)
44 if currentCondition == nil {
45 patch = append(patch, PatchOperation{Op: AddConditionPatch, After: &targetCondition})
46 continue
47 }
48
49 if !reflect.DeepEqual(&targetCondition, currentCondition) {
50 patch = append(patch, PatchOperation{Op: ChangeConditionPatch, After: &targetCondition, Before: currentCondition})
51 }
52 }
53
54
55 baseConditions := before.GetConditions()
56 for i := range baseConditions {
57 baseCondition := baseConditions[i]
58 targetCondition := Get(after, baseCondition.Type)
59 if targetCondition == nil {
60 patch = append(patch, PatchOperation{Op: RemoveConditionPatch, Before: &baseCondition})
61 }
62 }
63 return patch
64 }
65
66
67 type applyOptions struct {
68 ownedConditions []string
69 forceOverwrite bool
70 }
71
72 func (o *applyOptions) isOwnedCondition(t string) bool {
73 for _, i := range o.ownedConditions {
74 if i == t {
75 return true
76 }
77 }
78 return false
79 }
80
81
82 type ApplyOption func(*applyOptions)
83
84
85
86 func WithOwnedConditions(t ...string) ApplyOption {
87 return func(c *applyOptions) {
88 c.ownedConditions = t
89 }
90 }
91
92
93
94 func WithForceOverwrite(v bool) ApplyOption {
95 return func(c *applyOptions) {
96 c.forceOverwrite = v
97 }
98 }
99
100
101
102 func (p Patch) Apply(latest Setter, options ...ApplyOption) error {
103 if len(p) == 0 {
104 return nil
105 }
106
107 applyOpt := &applyOptions{}
108 for _, o := range options {
109 o(applyOpt)
110 }
111
112 for _, conditionPatch := range p {
113 switch conditionPatch.Op {
114 case AddConditionPatch:
115
116 if applyOpt.forceOverwrite || applyOpt.isOwnedCondition(conditionPatch.After.Type) {
117 Set(latest, conditionPatch.After)
118 continue
119 }
120
121
122 if latestCondition := Get(latest, conditionPatch.After.Type); latestCondition != nil {
123
124 if !hasSameState(latestCondition, conditionPatch.After) {
125 return errors.Errorf("error patching conditions: The condition %q was modified by a different process and this caused a merge/AddCondition conflict: %v", conditionPatch.After.Type, cmp.Diff(latestCondition, conditionPatch.After))
126 }
127
128
129 continue
130 }
131
132 Set(latest, conditionPatch.After)
133
134 case ChangeConditionPatch:
135
136 if applyOpt.forceOverwrite || applyOpt.isOwnedCondition(conditionPatch.After.Type) {
137 Set(latest, conditionPatch.After)
138 continue
139 }
140
141 latestCondition := Get(latest, conditionPatch.After.Type)
142
143
144 if latestCondition == nil {
145 return errors.Errorf("error patching conditions: The condition %q was deleted by a different process and this caused a merge/ChangeCondition conflict", conditionPatch.After.Type)
146 }
147
148
149
150 if !reflect.DeepEqual(latestCondition, conditionPatch.Before) {
151 if !hasSameState(latestCondition, conditionPatch.After) {
152 return errors.Errorf("error patching conditions: The condition %q was modified by a different process and this caused a merge/ChangeCondition conflict: %v", conditionPatch.After.Type, cmp.Diff(latestCondition, conditionPatch.After))
153 }
154
155
156 continue
157 }
158
159 Set(latest, conditionPatch.After)
160
161 case RemoveConditionPatch:
162
163 if applyOpt.forceOverwrite || applyOpt.isOwnedCondition(conditionPatch.Before.Type) {
164 Delete(latest, conditionPatch.Before.Type)
165 continue
166 }
167
168
169
170 if latestCondition := Get(latest, conditionPatch.Before.Type); latestCondition != nil {
171 if !hasSameState(latestCondition, conditionPatch.Before) {
172 return errors.Errorf("error patching conditions: The condition %q was modified by a different process and this caused a merge/RemoveCondition conflict: %v", conditionPatch.Before.Type, cmp.Diff(latestCondition, conditionPatch.Before))
173 }
174 }
175
176 Delete(latest, conditionPatch.Before.Type)
177 }
178 }
179 return nil
180 }
181
182
183 func (p Patch) IsZero() bool {
184 return len(p) == 0
185 }
186
View as plain text