4
4
package state
5
5
6
6
import (
7
+ "errors"
7
8
"fmt"
8
9
9
10
"github.com/hashicorp/consul/agent/structs"
10
11
"github.com/hashicorp/consul/api"
11
12
)
12
13
14
+ type UnsupportedFSMApplyPanicError struct {
15
+ Wrapped error
16
+ }
17
+
18
+ func (e * UnsupportedFSMApplyPanicError ) Unwrap () error {
19
+ return e .Wrapped
20
+ }
21
+
22
+ func (e * UnsupportedFSMApplyPanicError ) Error () string {
23
+ return e .Wrapped .Error ()
24
+ }
25
+
13
26
// txnKVS handles all KV-related operations.
14
27
func (s * Store ) txnKVS (tx WriteTxn , idx uint64 , op * structs.TxnKVOp ) (structs.TxnResults , error ) {
15
28
var entry * structs.DirEntry
16
29
var err error
17
30
31
+ // enumcover: api.KVOp
18
32
switch op .Verb {
19
33
case api .KVSet :
20
34
entry = & op .DirEnt
@@ -95,7 +109,7 @@ func (s *Store) txnKVS(tx WriteTxn, idx uint64, op *structs.TxnKVOp) (structs.Tx
95
109
}
96
110
97
111
default :
98
- err = fmt .Errorf ("unknown KV verb %q" , op .Verb )
112
+ err = & UnsupportedFSMApplyPanicError { fmt .Errorf ("unknown KV verb %q" , op .Verb )}
99
113
}
100
114
if err != nil {
101
115
return nil , err
@@ -123,11 +137,12 @@ func (s *Store) txnKVS(tx WriteTxn, idx uint64, op *structs.TxnKVOp) (structs.Tx
123
137
func txnSession (tx WriteTxn , idx uint64 , op * structs.TxnSessionOp ) error {
124
138
var err error
125
139
140
+ // enumcover: api.SessionOp
126
141
switch op .Verb {
127
142
case api .SessionDelete :
128
143
err = sessionDeleteWithSession (tx , & op .Session , idx )
129
144
default :
130
- err = fmt .Errorf ("unknown Session verb %q" , op .Verb )
145
+ return & UnsupportedFSMApplyPanicError { fmt .Errorf ("unknown session verb %q" , op .Verb )}
131
146
}
132
147
if err != nil {
133
148
return fmt .Errorf ("failed to delete session: %v" , err )
@@ -146,11 +161,17 @@ func txnLegacyIntention(tx WriteTxn, idx uint64, op *structs.TxnIntentionOp) err
146
161
case structs .IntentionOpDelete :
147
162
return legacyIntentionDeleteTxn (tx , idx , op .Intention .ID )
148
163
case structs .IntentionOpDeleteAll :
149
- fallthrough // deliberately not available via this api
164
+ // deliberately not available via this api
165
+ return fmt .Errorf ("Intention op not supported %q" , op .Op )
150
166
case structs .IntentionOpUpsert :
151
- fallthrough // deliberately not available via this api
167
+ // deliberately not available via this api
168
+ return fmt .Errorf ("Intention op not supported %q" , op .Op )
152
169
default :
153
- return fmt .Errorf ("unknown Intention op %q" , op .Op )
170
+ // If we've gotten to this point, the unknown verb has slipped by
171
+ // endpoint validation. This means it could be a mismatch in Server versions
172
+ // that are sending known verbs as part of Raft logs. We panic rather than silently
173
+ // swallowing the error during Raft Apply.
174
+ panic (fmt .Sprintf ("unknown Intention op %q" , op .Op ))
154
175
}
155
176
}
156
177
@@ -202,7 +223,7 @@ func (s *Store) txnNode(tx WriteTxn, idx uint64, op *structs.TxnNodeOp) (structs
202
223
}
203
224
204
225
default :
205
- err = fmt .Errorf ("unknown Node verb %q" , op .Verb )
226
+ err = & UnsupportedFSMApplyPanicError { fmt .Errorf ("unknown Node verb %q" , op .Verb )}
206
227
}
207
228
if err != nil {
208
229
return nil , err
@@ -271,7 +292,7 @@ func (s *Store) txnService(tx WriteTxn, idx uint64, op *structs.TxnServiceOp) (s
271
292
return nil , err
272
293
273
294
default :
274
- return nil , fmt .Errorf ("unknown Service verb %q" , op .Verb )
295
+ return nil , & UnsupportedFSMApplyPanicError { fmt .Errorf ("unknown Service verb %q" , op .Verb )}
275
296
}
276
297
}
277
298
@@ -326,7 +347,7 @@ func (s *Store) txnCheck(tx WriteTxn, idx uint64, op *structs.TxnCheckOp) (struc
326
347
}
327
348
328
349
default :
329
- err = fmt .Errorf ("unknown Check verb %q" , op .Verb )
350
+ err = & UnsupportedFSMApplyPanicError { fmt .Errorf ("unknown check verb %q" , op .Verb )}
330
351
}
331
352
if err != nil {
332
353
return nil , err
@@ -352,7 +373,7 @@ func (s *Store) txnCheck(tx WriteTxn, idx uint64, op *structs.TxnCheckOp) (struc
352
373
// txnDispatch runs the given operations inside the state store transaction.
353
374
func (s * Store ) txnDispatch (tx WriteTxn , idx uint64 , ops structs.TxnOps ) (structs.TxnResults , structs.TxnErrors ) {
354
375
results := make (structs.TxnResults , 0 , len (ops ))
355
- errors := make (structs.TxnErrors , 0 , len (ops ))
376
+ errs := make (structs.TxnErrors , 0 , len (ops ))
356
377
for i , op := range ops {
357
378
var ret structs.TxnResults
358
379
var err error
@@ -374,24 +395,33 @@ func (s *Store) txnDispatch(tx WriteTxn, idx uint64, ops structs.TxnOps) (struct
374
395
// compatibility with pre-1.9.0 raft logs and during upgrades.
375
396
err = txnLegacyIntention (tx , idx , op .Intention )
376
397
default :
377
- err = fmt . Errorf ("no operation specified" )
398
+ panic ("no operation specified" )
378
399
}
379
400
380
401
// Accumulate the results.
381
402
results = append (results , ret ... )
382
403
404
+ var panicErr * UnsupportedFSMApplyPanicError
405
+ if errors .As (err , & panicErr ) {
406
+ // If we've gotten to this point, the unknown verb has slipped by
407
+ // endpoint validation. This means it could be a mismatch in Server versions
408
+ // that are sending known verbs as part of Raft logs. We panic rather than silently
409
+ // swallowing the error during Raft Apply. See NET-9016 for historical context.
410
+ panic (panicErr .Wrapped )
411
+ }
412
+
383
413
// Capture any error along with the index of the operation that
384
414
// failed.
385
415
if err != nil {
386
- errors = append (errors , & structs.TxnError {
416
+ errs = append (errs , & structs.TxnError {
387
417
OpIndex : i ,
388
418
What : err .Error (),
389
419
})
390
420
}
391
421
}
392
422
393
- if len (errors ) > 0 {
394
- return nil , errors
423
+ if len (errs ) > 0 {
424
+ return nil , errs
395
425
}
396
426
397
427
return results , nil
0 commit comments