5
5
"context"
6
6
"encoding/hex"
7
7
"encoding/json"
8
+ "errors"
8
9
"fmt"
10
+ "net"
9
11
"net/http"
10
12
"reflect"
11
13
"sync"
@@ -16,6 +18,7 @@ import (
16
18
"github.com/kong/deck/file"
17
19
"github.com/kong/deck/state"
18
20
deckutils "github.com/kong/deck/utils"
21
+ "github.com/kong/go-kong/kong"
19
22
"github.com/prometheus/client_golang/prometheus"
20
23
"github.com/sirupsen/logrus"
21
24
@@ -85,8 +88,9 @@ func PerformUpdate(ctx context.Context,
85
88
86
89
if err != nil {
87
90
promMetrics .ConfigPushCount .With (prometheus.Labels {
88
- metrics .SuccessKey : metrics .SuccessFalse ,
89
- metrics .ProtocolKey : metricsProtocol ,
91
+ metrics .SuccessKey : metrics .SuccessFalse ,
92
+ metrics .ProtocolKey : metricsProtocol ,
93
+ metrics .FailureReasonKey : pushFailureReason (err ),
90
94
}).Inc ()
91
95
promMetrics .ConfigPushDuration .With (prometheus.Labels {
92
96
metrics .SuccessKey : metrics .SuccessFalse ,
@@ -96,8 +100,9 @@ func PerformUpdate(ctx context.Context,
96
100
}
97
101
98
102
promMetrics .ConfigPushCount .With (prometheus.Labels {
99
- metrics .SuccessKey : metrics .SuccessTrue ,
100
- metrics .ProtocolKey : metricsProtocol ,
103
+ metrics .SuccessKey : metrics .SuccessTrue ,
104
+ metrics .ProtocolKey : metricsProtocol ,
105
+ metrics .FailureReasonKey : "" ,
101
106
}).Inc ()
102
107
promMetrics .ConfigPushDuration .With (prometheus.Labels {
103
108
metrics .SuccessKey : metrics .SuccessTrue ,
@@ -204,45 +209,55 @@ func onUpdateDBMode(ctx context.Context,
204
209
skipCACertificates bool ,
205
210
) error {
206
211
dumpConfig := dump.Config {SelectorTags : selectorTags , SkipCACerts : skipCACertificates }
207
- // read the current state
208
- rawState , err := dump .Get (ctx , kongConfig .Client , dumpConfig )
209
- if err != nil {
210
- return fmt .Errorf ("loading configuration from kong: %w" , err )
211
- }
212
- currentState , err := state .Get (rawState )
213
- if err != nil {
214
- return err
215
- }
216
212
217
- // read the target state
218
- rawState , err = file .Get (ctx , targetContent , file.RenderConfig {
219
- CurrentState : currentState ,
220
- KongVersion : kongConfig .Version ,
221
- }, dumpConfig , kongConfig .Client )
213
+ cs , err := currentState (ctx , kongConfig , dumpConfig )
222
214
if err != nil {
223
215
return err
224
216
}
225
- targetState , err := state .Get (rawState )
217
+
218
+ ts , err := targetState (ctx , targetContent , cs , kongConfig , dumpConfig )
226
219
if err != nil {
227
- return err
220
+ return deckConfigConflictError { err }
228
221
}
229
222
230
223
syncer , err := diff .NewSyncer (diff.SyncerOpts {
231
- CurrentState : currentState ,
232
- TargetState : targetState ,
224
+ CurrentState : cs ,
225
+ TargetState : ts ,
233
226
KongClient : kongConfig .Client ,
234
227
SilenceWarnings : true ,
235
228
})
236
229
if err != nil {
237
230
return fmt .Errorf ("creating a new syncer: %w" , err )
238
231
}
232
+
239
233
_ , errs := syncer .Solve (ctx , kongConfig .Concurrency , false )
240
234
if errs != nil {
241
235
return deckutils.ErrArray {Errors : errs }
242
236
}
243
237
return nil
244
238
}
245
239
240
+ func currentState (ctx context.Context , kongConfig * Kong , dumpConfig dump.Config ) (* state.KongState , error ) {
241
+ rawState , err := dump .Get (ctx , kongConfig .Client , dumpConfig )
242
+ if err != nil {
243
+ return nil , fmt .Errorf ("loading configuration from kong: %w" , err )
244
+ }
245
+
246
+ return state .Get (rawState )
247
+ }
248
+
249
+ func targetState (ctx context.Context , targetContent * file.Content , currentState * state.KongState , kongConfig * Kong , dumpConfig dump.Config ) (* state.KongState , error ) {
250
+ rawState , err := file .Get (ctx , targetContent , file.RenderConfig {
251
+ CurrentState : currentState ,
252
+ KongVersion : kongConfig .Version ,
253
+ }, dumpConfig , kongConfig .Client )
254
+ if err != nil {
255
+ return nil , err
256
+ }
257
+
258
+ return state .Get (rawState )
259
+ }
260
+
246
261
func equalSHA (a , b []byte ) bool {
247
262
return reflect .DeepEqual (a , b )
248
263
}
@@ -273,3 +288,55 @@ func hasSHAUpdateAlreadyBeenReported(latestUpdateSHA []byte) bool {
273
288
latestReportedSHA = latestUpdateSHA
274
289
return false
275
290
}
291
+
292
+ // deckConfigConflictError is an error used to wrap deck config conflict errors returned from deck functions
293
+ // transforming KongRawState to KongState (e.g. state.Get, dump.Get).
294
+ type deckConfigConflictError struct {
295
+ err error
296
+ }
297
+
298
+ func (e deckConfigConflictError ) Error () string {
299
+ return e .err .Error ()
300
+ }
301
+
302
+ func (e deckConfigConflictError ) Is (target error ) bool {
303
+ _ , ok := target .(deckConfigConflictError )
304
+ return ok
305
+ }
306
+
307
+ func (e deckConfigConflictError ) Unwrap () error {
308
+ return e .err
309
+ }
310
+
311
+ // pushFailureReason extracts config push failure reason from an error returned from onUpdateInMemoryMode or onUpdateDBMode.
312
+ func pushFailureReason (err error ) string {
313
+ var netErr net.Error
314
+ if errors .As (err , & netErr ) {
315
+ return metrics .FailureReasonNetwork
316
+ }
317
+
318
+ if isConflictErr (err ) {
319
+ return metrics .FailureReasonConflict
320
+ }
321
+
322
+ return metrics .FailureReasonOther
323
+ }
324
+
325
+ func isConflictErr (err error ) bool {
326
+ var apiErr * kong.APIError
327
+ if errors .As (err , & apiErr ) && apiErr .Code () == http .StatusConflict ||
328
+ errors .Is (err , deckConfigConflictError {}) {
329
+ return true
330
+ }
331
+
332
+ var deckErrArray deckutils.ErrArray
333
+ if errors .As (err , & deckErrArray ) {
334
+ for _ , err := range deckErrArray .Errors {
335
+ if isConflictErr (err ) {
336
+ return true
337
+ }
338
+ }
339
+ }
340
+
341
+ return false
342
+ }
0 commit comments