@@ -27,6 +27,7 @@ import (
27
27
"strconv"
28
28
"strings"
29
29
"sync"
30
+ "sync/atomic"
30
31
"testing"
31
32
32
33
"github.com/coreos/pkg/capnslog"
@@ -71,9 +72,11 @@ type prefixTransformer struct {
71
72
prefix []byte
72
73
stale bool
73
74
err error
75
+ reads uint64
74
76
}
75
77
76
- func (p prefixTransformer ) TransformFromStorage (b []byte , ctx value.Context ) ([]byte , bool , error ) {
78
+ func (p * prefixTransformer ) TransformFromStorage (b []byte , ctx value.Context ) ([]byte , bool , error ) {
79
+ atomic .AddUint64 (& p .reads , 1 )
77
80
if ctx == nil {
78
81
panic ("no context provided" )
79
82
}
@@ -82,7 +85,7 @@ func (p prefixTransformer) TransformFromStorage(b []byte, ctx value.Context) ([]
82
85
}
83
86
return bytes .TrimPrefix (b , p .prefix ), p .stale , p .err
84
87
}
85
- func (p prefixTransformer ) TransformToStorage (b []byte , ctx value.Context ) ([]byte , error ) {
88
+ func (p * prefixTransformer ) TransformToStorage (b []byte , ctx value.Context ) ([]byte , error ) {
86
89
if ctx == nil {
87
90
panic ("no context provided" )
88
91
}
@@ -92,6 +95,10 @@ func (p prefixTransformer) TransformToStorage(b []byte, ctx value.Context) ([]by
92
95
return b , p .err
93
96
}
94
97
98
+ func (p * prefixTransformer ) resetReads () {
99
+ p .reads = 0
100
+ }
101
+
95
102
func TestCreate (t * testing.T ) {
96
103
ctx , store , cluster := testSetup (t )
97
104
defer cluster .Terminate (t )
@@ -502,11 +509,11 @@ func TestGuaranteedUpdate(t *testing.T) {
502
509
if tt .expectNoUpdate {
503
510
name = storeObj .Name
504
511
}
505
- originalTransformer := store .transformer .(prefixTransformer )
512
+ originalTransformer := store .transformer .(* prefixTransformer )
506
513
if tt .transformStale {
507
- transformer := originalTransformer
514
+ transformer := * originalTransformer
508
515
transformer .stale = true
509
- store .transformer = transformer
516
+ store .transformer = & transformer
510
517
}
511
518
version := storeObj .ResourceVersion
512
519
err := store .GuaranteedUpdate (ctx , tt .key , out , tt .ignoreNotFound , tt .precondition ,
@@ -606,7 +613,7 @@ func TestGuaranteedUpdateChecksStoredData(t *testing.T) {
606
613
t .Fatal (err )
607
614
}
608
615
609
- store .transformer = prefixTransformer {prefix : []byte (defaultTestPrefix )}
616
+ store .transformer = & prefixTransformer {prefix : []byte (defaultTestPrefix )}
610
617
611
618
// this update should write the canonical value to etcd because the new serialization differs
612
619
// from the stored serialization
@@ -639,7 +646,7 @@ func TestGuaranteedUpdateChecksStoredData(t *testing.T) {
639
646
t .Errorf ("guaranteed update should have short-circuited write, got %#v" , out )
640
647
}
641
648
642
- store .transformer = prefixTransformer {prefix : []byte (defaultTestPrefix ), stale : true }
649
+ store .transformer = & prefixTransformer {prefix : []byte (defaultTestPrefix ), stale : true }
643
650
644
651
// this update should write to etcd because the transformer reported stale
645
652
err = store .GuaranteedUpdate (ctx , key , out , true , nil ,
@@ -752,7 +759,7 @@ func TestTransformationFailure(t *testing.T) {
752
759
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
753
760
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
754
761
defer cluster .Terminate (t )
755
- store := newStore (cluster .RandClient (), false , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
762
+ store := newStore (cluster .RandClient (), false , codec , "" , & prefixTransformer {prefix : []byte (defaultTestPrefix )})
756
763
ctx := context .Background ()
757
764
758
765
preset := []struct {
@@ -782,7 +789,7 @@ func TestTransformationFailure(t *testing.T) {
782
789
783
790
// create a second resource with an invalid prefix
784
791
oldTransformer := store .transformer
785
- store .transformer = prefixTransformer {prefix : []byte ("otherprefix!" )}
792
+ store .transformer = & prefixTransformer {prefix : []byte ("otherprefix!" )}
786
793
for i , ps := range preset [1 :] {
787
794
preset [1 :][i ].storedObj = & example.Pod {}
788
795
err := store .Create (ctx , ps .key , ps .obj , preset [1 :][i ].storedObj , 0 )
@@ -829,8 +836,8 @@ func TestList(t *testing.T) {
829
836
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
830
837
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
831
838
defer cluster .Terminate (t )
832
- store := newStore (cluster .RandClient (), true , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
833
- disablePagingStore := newStore (cluster .RandClient (), false , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
839
+ store := newStore (cluster .RandClient (), true , codec , "" , & prefixTransformer {prefix : []byte (defaultTestPrefix )})
840
+ disablePagingStore := newStore (cluster .RandClient (), false , codec , "" , & prefixTransformer {prefix : []byte (defaultTestPrefix )})
834
841
ctx := context .Background ()
835
842
836
843
// Setup storage with the following structure:
@@ -1182,7 +1189,11 @@ func TestListContinuation(t *testing.T) {
1182
1189
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
1183
1190
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
1184
1191
defer cluster .Terminate (t )
1185
- store := newStore (cluster .RandClient (), true , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
1192
+ transformer := & prefixTransformer {prefix : []byte (defaultTestPrefix )}
1193
+ etcdClient := cluster .RandClient ()
1194
+ recorder := & clientRecorder {KV : etcdClient .KV }
1195
+ etcdClient .KV = recorder
1196
+ store := newStore (etcdClient , true , codec , "" , transformer )
1186
1197
ctx := context .Background ()
1187
1198
1188
1199
// Setup storage with the following structure:
@@ -1247,6 +1258,14 @@ func TestListContinuation(t *testing.T) {
1247
1258
if len (out .Items ) != 1 || ! reflect .DeepEqual (& out .Items [0 ], preset [0 ].storedObj ) {
1248
1259
t .Fatalf ("Unexpected first page: %#v" , out .Items )
1249
1260
}
1261
+ if transformer .reads != 1 {
1262
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1263
+ }
1264
+ if recorder .reads != 1 {
1265
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1266
+ }
1267
+ transformer .resetReads ()
1268
+ recorder .resetReads ()
1250
1269
1251
1270
continueFromSecondItem := out .Continue
1252
1271
@@ -1263,6 +1282,14 @@ func TestListContinuation(t *testing.T) {
1263
1282
t .Logf ("continue token was %d %s %v" , rv , key , err )
1264
1283
t .Fatalf ("Unexpected second page: %#v" , out .Items )
1265
1284
}
1285
+ if transformer .reads != 2 {
1286
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1287
+ }
1288
+ if recorder .reads != 1 {
1289
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1290
+ }
1291
+ transformer .resetReads ()
1292
+ recorder .resetReads ()
1266
1293
1267
1294
// limit, should get two more pages
1268
1295
out = & example.PodList {}
@@ -1275,7 +1302,17 @@ func TestListContinuation(t *testing.T) {
1275
1302
if len (out .Items ) != 1 || ! reflect .DeepEqual (& out .Items [0 ], preset [1 ].storedObj ) {
1276
1303
t .Fatalf ("Unexpected second page: %#v" , out .Items )
1277
1304
}
1305
+ if transformer .reads != 1 {
1306
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1307
+ }
1308
+ if recorder .reads != 1 {
1309
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1310
+ }
1311
+ transformer .resetReads ()
1312
+ recorder .resetReads ()
1313
+
1278
1314
continueFromThirdItem := out .Continue
1315
+
1279
1316
out = & example.PodList {}
1280
1317
if err := store .List (ctx , "/" , "0" , pred (1 , continueFromThirdItem ), out ); err != nil {
1281
1318
t .Fatalf ("Unable to get second page: %v" , err )
@@ -1286,14 +1323,142 @@ func TestListContinuation(t *testing.T) {
1286
1323
if len (out .Items ) != 1 || ! reflect .DeepEqual (& out .Items [0 ], preset [2 ].storedObj ) {
1287
1324
t .Fatalf ("Unexpected third page: %#v" , out .Items )
1288
1325
}
1326
+ if transformer .reads != 1 {
1327
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1328
+ }
1329
+ if recorder .reads != 1 {
1330
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1331
+ }
1332
+ transformer .resetReads ()
1333
+ recorder .resetReads ()
1334
+ }
1335
+
1336
+ type clientRecorder struct {
1337
+ reads uint64
1338
+ clientv3.KV
1339
+ }
1289
1340
1341
+ func (r * clientRecorder ) Get (ctx context.Context , key string , opts ... clientv3.OpOption ) (* clientv3.GetResponse , error ) {
1342
+ atomic .AddUint64 (& r .reads , 1 )
1343
+ return r .KV .Get (ctx , key , opts ... )
1344
+ }
1345
+
1346
+ func (r * clientRecorder ) resetReads () {
1347
+ r .reads = 0
1348
+ }
1349
+
1350
+ func TestListContinuationWithFilter (t * testing.T ) {
1351
+ codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
1352
+ cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
1353
+ defer cluster .Terminate (t )
1354
+ transformer := & prefixTransformer {prefix : []byte (defaultTestPrefix )}
1355
+ etcdClient := cluster .RandClient ()
1356
+ recorder := & clientRecorder {KV : etcdClient .KV }
1357
+ etcdClient .KV = recorder
1358
+ store := newStore (etcdClient , true , codec , "" , transformer )
1359
+ ctx := context .Background ()
1360
+
1361
+ preset := []struct {
1362
+ key string
1363
+ obj * example.Pod
1364
+ storedObj * example.Pod
1365
+ }{
1366
+ {
1367
+ key : "/1" ,
1368
+ obj : & example.Pod {ObjectMeta : metav1.ObjectMeta {Name : "foo" }},
1369
+ },
1370
+ {
1371
+ key : "/2" ,
1372
+ obj : & example.Pod {ObjectMeta : metav1.ObjectMeta {Name : "bar" }}, // this should not match
1373
+ },
1374
+ {
1375
+ key : "/3" ,
1376
+ obj : & example.Pod {ObjectMeta : metav1.ObjectMeta {Name : "foo" }},
1377
+ },
1378
+ {
1379
+ key : "/4" ,
1380
+ obj : & example.Pod {ObjectMeta : metav1.ObjectMeta {Name : "foo" }},
1381
+ },
1382
+ }
1383
+
1384
+ for i , ps := range preset {
1385
+ preset [i ].storedObj = & example.Pod {}
1386
+ err := store .Create (ctx , ps .key , ps .obj , preset [i ].storedObj , 0 )
1387
+ if err != nil {
1388
+ t .Fatalf ("Set failed: %v" , err )
1389
+ }
1390
+ }
1391
+
1392
+ // the first list call should try to get 2 items from etcd (and only those items should be returned)
1393
+ // the field selector should result in it reading 3 items via the transformer
1394
+ // the chunking should result in 2 etcd Gets
1395
+ // there should be a continueValue because there is more data
1396
+ out := & example.PodList {}
1397
+ pred := func (limit int64 , continueValue string ) storage.SelectionPredicate {
1398
+ return storage.SelectionPredicate {
1399
+ Limit : limit ,
1400
+ Continue : continueValue ,
1401
+ Label : labels .Everything (),
1402
+ Field : fields .OneTermNotEqualSelector ("metadata.name" , "bar" ),
1403
+ GetAttrs : func (obj runtime.Object ) (labels.Set , fields.Set , error ) {
1404
+ pod := obj .(* example.Pod )
1405
+ return nil , fields.Set {"metadata.name" : pod .Name }, nil
1406
+ },
1407
+ }
1408
+ }
1409
+ if err := store .List (ctx , "/" , "0" , pred (2 , "" ), out ); err != nil {
1410
+ t .Errorf ("Unable to get initial list: %v" , err )
1411
+ }
1412
+ if len (out .Continue ) == 0 {
1413
+ t .Errorf ("No continuation token set" )
1414
+ }
1415
+ if len (out .Items ) != 2 || ! reflect .DeepEqual (& out .Items [0 ], preset [0 ].storedObj ) || ! reflect .DeepEqual (& out .Items [1 ], preset [2 ].storedObj ) {
1416
+ t .Errorf ("Unexpected first page, len=%d: %#v" , len (out .Items ), out .Items )
1417
+ }
1418
+ if transformer .reads != 3 {
1419
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1420
+ }
1421
+ if recorder .reads != 2 {
1422
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1423
+ }
1424
+ transformer .resetReads ()
1425
+ recorder .resetReads ()
1426
+
1427
+ // the rest of the test does not make sense if the previous call failed
1428
+ if t .Failed () {
1429
+ return
1430
+ }
1431
+
1432
+ cont := out .Continue
1433
+
1434
+ // the second list call should try to get 2 more items from etcd
1435
+ // but since there is only one item left, that is all we should get with no continueValue
1436
+ // both read counters should be incremented for the singular calls they make in this case
1437
+ out = & example.PodList {}
1438
+ if err := store .List (ctx , "/" , "0" , pred (2 , cont ), out ); err != nil {
1439
+ t .Errorf ("Unable to get second page: %v" , err )
1440
+ }
1441
+ if len (out .Continue ) != 0 {
1442
+ t .Errorf ("Unexpected continuation token set" )
1443
+ }
1444
+ if len (out .Items ) != 1 || ! reflect .DeepEqual (& out .Items [0 ], preset [3 ].storedObj ) {
1445
+ t .Errorf ("Unexpected second page, len=%d: %#v" , len (out .Items ), out .Items )
1446
+ }
1447
+ if transformer .reads != 1 {
1448
+ t .Errorf ("unexpected reads: %d" , transformer .reads )
1449
+ }
1450
+ if recorder .reads != 1 {
1451
+ t .Errorf ("unexpected reads: %d" , recorder .reads )
1452
+ }
1453
+ transformer .resetReads ()
1454
+ recorder .resetReads ()
1290
1455
}
1291
1456
1292
1457
func TestListInconsistentContinuation (t * testing.T ) {
1293
1458
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
1294
1459
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
1295
1460
defer cluster .Terminate (t )
1296
- store := newStore (cluster .RandClient (), true , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
1461
+ store := newStore (cluster .RandClient (), true , codec , "" , & prefixTransformer {prefix : []byte (defaultTestPrefix )})
1297
1462
ctx := context .Background ()
1298
1463
1299
1464
// Setup storage with the following structure:
@@ -1438,7 +1603,7 @@ func TestListInconsistentContinuation(t *testing.T) {
1438
1603
func testSetup (t * testing.T ) (context.Context , * store , * integration.ClusterV3 ) {
1439
1604
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
1440
1605
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
1441
- store := newStore (cluster .RandClient (), true , codec , "" , prefixTransformer {prefix : []byte (defaultTestPrefix )})
1606
+ store := newStore (cluster .RandClient (), true , codec , "" , & prefixTransformer {prefix : []byte (defaultTestPrefix )})
1442
1607
ctx := context .Background ()
1443
1608
// As 30s is the default timeout for testing in glboal configuration,
1444
1609
// we cannot wait longer than that in a single time: change it to 10
@@ -1471,7 +1636,7 @@ func TestPrefix(t *testing.T) {
1471
1636
codec := apitesting .TestCodec (codecs , examplev1 .SchemeGroupVersion )
1472
1637
cluster := integration .NewClusterV3 (t , & integration.ClusterConfig {Size : 1 })
1473
1638
defer cluster .Terminate (t )
1474
- transformer := prefixTransformer {prefix : []byte (defaultTestPrefix )}
1639
+ transformer := & prefixTransformer {prefix : []byte (defaultTestPrefix )}
1475
1640
testcases := map [string ]string {
1476
1641
"custom/prefix" : "/custom/prefix" ,
1477
1642
"/custom//prefix//" : "/custom/prefix" ,
0 commit comments