1
2
3
4 package filter
5
6 import (
7 "fmt"
8 "strings"
9
10 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11 "sigs.k8s.io/cli-utils/pkg/apis/actuation"
12 "sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
13 "sigs.k8s.io/cli-utils/pkg/common"
14 "sigs.k8s.io/cli-utils/pkg/object"
15 )
16
17
18 type Relationship int
19
20 const (
21 RelationshipDependent Relationship = iota
22 RelationshipDependency
23 )
24
25
26 type Phase int
27
28 const (
29 PhaseActuation Phase = iota
30 PhaseReconcile
31 )
32
33
34
35 type DependencyFilter struct {
36 TaskContext *taskrunner.TaskContext
37 ActuationStrategy actuation.ActuationStrategy
38 DryRunStrategy common.DryRunStrategy
39 }
40
41 const DependencyFilterName = "DependencyFilter"
42
43
44 func (dnrf DependencyFilter) Name() string {
45 return DependencyFilterName
46 }
47
48
49
50
51
52
53 func (dnrf DependencyFilter) Filter(obj *unstructured.Unstructured) error {
54 id := object.UnstructuredToObjMetadata(obj)
55
56 switch dnrf.ActuationStrategy {
57 case actuation.ActuationStrategyApply:
58
59 for _, depID := range dnrf.TaskContext.Graph().Dependencies(id) {
60 err := dnrf.filterByRelationship(id, depID, RelationshipDependency)
61 if err != nil {
62 return err
63 }
64 }
65 case actuation.ActuationStrategyDelete:
66
67 for _, depID := range dnrf.TaskContext.Graph().Dependents(id) {
68 err := dnrf.filterByRelationship(id, depID, RelationshipDependent)
69 if err != nil {
70 return err
71 }
72 }
73 default:
74 return NewFatalError(fmt.Errorf("invalid actuation strategy: %q", dnrf.ActuationStrategy))
75 }
76 return nil
77 }
78
79 func (dnrf DependencyFilter) filterByRelationship(aID, bID object.ObjMetadata, relationship Relationship) error {
80
81
82
83
84 if dnrf.TaskContext.IsInvalidObject(bID) {
85
86 return NewFatalError(fmt.Errorf("invalid %s: %s",
87 strings.ToLower(relationship.String()),
88 bID))
89 }
90
91 status, found := dnrf.TaskContext.InventoryManager().ObjectStatus(bID)
92 if !found {
93
94
95
96 return NewFatalError(fmt.Errorf("unknown %s actuation strategy: %s",
97 strings.ToLower(relationship.String()), bID))
98 }
99
100
101
102 if status.Strategy != dnrf.ActuationStrategy {
103
104 return &DependencyActuationMismatchError{
105 Object: aID,
106 Strategy: dnrf.ActuationStrategy,
107 Relationship: relationship,
108 Relation: bID,
109 RelationStrategy: status.Strategy,
110 }
111 }
112
113 switch status.Actuation {
114 case actuation.ActuationPending:
115
116 return NewFatalError(fmt.Errorf("premature %s: %s %s actuation %s: %s",
117 strings.ToLower(dnrf.ActuationStrategy.String()),
118 strings.ToLower(relationship.String()),
119 strings.ToLower(status.Strategy.String()),
120 strings.ToLower(status.Actuation.String()),
121 bID))
122 case actuation.ActuationSkipped, actuation.ActuationFailed:
123
124 return &DependencyPreventedActuationError{
125 Object: aID,
126 Strategy: dnrf.ActuationStrategy,
127 Relationship: relationship,
128 Relation: bID,
129 RelationPhase: PhaseActuation,
130 RelationActuationStatus: status.Actuation,
131 RelationReconcileStatus: status.Reconcile,
132 }
133 case actuation.ActuationSucceeded:
134
135 default:
136
137 return NewFatalError(fmt.Errorf("invalid %s actuation status %q: %s",
138 strings.ToLower(relationship.String()),
139 strings.ToLower(status.Actuation.String()),
140 bID))
141 }
142
143
144 if dnrf.DryRunStrategy.ClientOrServerDryRun() {
145
146 return nil
147 }
148
149 switch status.Reconcile {
150 case actuation.ReconcilePending:
151
152 return NewFatalError(fmt.Errorf("premature %s: %s %s reconcile %s: %s",
153 strings.ToLower(dnrf.ActuationStrategy.String()),
154 strings.ToLower(relationship.String()),
155 strings.ToLower(status.Strategy.String()),
156 strings.ToLower(status.Reconcile.String()),
157 bID))
158 case actuation.ReconcileSkipped, actuation.ReconcileFailed, actuation.ReconcileTimeout:
159
160 return &DependencyPreventedActuationError{
161 Object: aID,
162 Strategy: dnrf.ActuationStrategy,
163 Relationship: relationship,
164 Relation: bID,
165 RelationPhase: PhaseReconcile,
166 RelationActuationStatus: status.Actuation,
167 RelationReconcileStatus: status.Reconcile,
168 }
169 case actuation.ReconcileSucceeded:
170
171 default:
172
173 return NewFatalError(fmt.Errorf("invalid %s reconcile status %q: %s",
174 strings.ToLower(relationship.String()),
175 strings.ToLower(status.Reconcile.String()),
176 bID))
177 }
178
179
180 return nil
181 }
182
183 type DependencyPreventedActuationError struct {
184 Object object.ObjMetadata
185 Strategy actuation.ActuationStrategy
186 Relationship Relationship
187
188 Relation object.ObjMetadata
189 RelationPhase Phase
190 RelationActuationStatus actuation.ActuationStatus
191 RelationReconcileStatus actuation.ReconcileStatus
192 }
193
194 func (e *DependencyPreventedActuationError) Error() string {
195 switch e.RelationPhase {
196 case PhaseActuation:
197 return fmt.Sprintf("%s %s %s %s: %s",
198 strings.ToLower(e.Relationship.String()),
199 strings.ToLower(e.Strategy.String()),
200 strings.ToLower(e.RelationPhase.String()),
201 strings.ToLower(e.RelationActuationStatus.String()),
202 e.Relation)
203 case PhaseReconcile:
204 return fmt.Sprintf("%s %s %s %s: %s",
205 strings.ToLower(e.Relationship.String()),
206 strings.ToLower(e.Strategy.String()),
207 strings.ToLower(e.RelationPhase.String()),
208 strings.ToLower(e.RelationReconcileStatus.String()),
209 e.Relation)
210 default:
211 return fmt.Sprintf("invalid phase: %s", e.RelationPhase)
212 }
213 }
214
215 func (e *DependencyPreventedActuationError) Is(err error) bool {
216 if err == nil {
217 return false
218 }
219 tErr, ok := err.(*DependencyPreventedActuationError)
220 if !ok {
221 return false
222 }
223 return e.Object == tErr.Object &&
224 e.Strategy == tErr.Strategy &&
225 e.Relationship == tErr.Relationship &&
226 e.Relation == tErr.Relation &&
227 e.RelationPhase == tErr.RelationPhase &&
228 e.RelationActuationStatus == tErr.RelationActuationStatus &&
229 e.RelationReconcileStatus == tErr.RelationReconcileStatus
230 }
231
232 type DependencyActuationMismatchError struct {
233 Object object.ObjMetadata
234 Strategy actuation.ActuationStrategy
235 Relationship Relationship
236
237 Relation object.ObjMetadata
238 RelationStrategy actuation.ActuationStrategy
239 }
240
241 func (e *DependencyActuationMismatchError) Error() string {
242 return fmt.Sprintf("%s scheduled for %s: %s",
243 strings.ToLower(e.Relationship.String()),
244 strings.ToLower(e.RelationStrategy.String()),
245 e.Relation)
246 }
247
248 func (e *DependencyActuationMismatchError) Is(err error) bool {
249 if err == nil {
250 return false
251 }
252 tErr, ok := err.(*DependencyActuationMismatchError)
253 if !ok {
254 return false
255 }
256 return e.Object == tErr.Object &&
257 e.Strategy == tErr.Strategy &&
258 e.Relationship == tErr.Relationship &&
259 e.Relation == tErr.Relation &&
260 e.RelationStrategy == tErr.RelationStrategy
261 }
262
View as plain text