@@ -1062,7 +1062,7 @@ func TestOperatorUpgrade(t *testing.T) {
1062
1062
Resolver : solver .NewDeppySolver (controllers .NewVariableSource (cl , & fakeCatalogClient )),
1063
1063
}
1064
1064
1065
- t .Run ("semver upgrade constraints" , func (t * testing.T ) {
1065
+ t .Run ("semver upgrade constraints enforcement of upgrades within major version " , func (t * testing.T ) {
1066
1066
defer featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .ForceSemverUpgradeConstraints , true )()
1067
1067
defer func () {
1068
1068
require .NoError (t , cl .DeleteAllOf (ctx , & operatorsv1alpha1.Operator {}))
@@ -1155,7 +1155,7 @@ func TestOperatorUpgrade(t *testing.T) {
1155
1155
assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1156
1156
})
1157
1157
1158
- t .Run ("legacy semantics upgrade constraints" , func (t * testing.T ) {
1158
+ t .Run ("legacy semantics upgrade constraints enforcement " , func (t * testing.T ) {
1159
1159
defer featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .ForceSemverUpgradeConstraints , false )()
1160
1160
defer func () {
1161
1161
require .NoError (t , cl .DeleteAllOf (ctx , & operatorsv1alpha1.Operator {}))
@@ -1247,6 +1247,262 @@ func TestOperatorUpgrade(t *testing.T) {
1247
1247
assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1248
1248
assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1249
1249
})
1250
+
1251
+ t .Run ("ignore upgrade constraints" , func (t * testing.T ) {
1252
+ for _ , tt := range []struct {
1253
+ name string
1254
+ flagState bool
1255
+ }{
1256
+ {
1257
+ name : "ForceSemverUpgradeConstraints feature gate enabled" ,
1258
+ flagState : true ,
1259
+ },
1260
+ {
1261
+ name : "ForceSemverUpgradeConstraints feature gate disabled" ,
1262
+ flagState : false ,
1263
+ },
1264
+ } {
1265
+ t .Run (tt .name , func (t * testing.T ) {
1266
+ defer featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .ForceSemverUpgradeConstraints , tt .flagState )()
1267
+ defer func () {
1268
+ require .NoError (t , cl .DeleteAllOf (ctx , & operatorsv1alpha1.Operator {}))
1269
+ require .NoError (t , cl .DeleteAllOf (ctx , & rukpakv1alpha1.BundleDeployment {}))
1270
+ }()
1271
+
1272
+ opKey := types.NamespacedName {Name : fmt .Sprintf ("operator-test-%s" , rand .String (8 ))}
1273
+ operator := & operatorsv1alpha1.Operator {
1274
+ ObjectMeta : metav1.ObjectMeta {Name : opKey .Name },
1275
+ Spec : operatorsv1alpha1.OperatorSpec {
1276
+ PackageName : "prometheus" ,
1277
+ Version : "1.0.0" ,
1278
+ Channel : "beta" ,
1279
+ UpgradeConstraintPolicy : operatorsv1alpha1 .UpgradeConstraintPolicyIgnore ,
1280
+ },
1281
+ }
1282
+ // Create an operator
1283
+ err := cl .Create (ctx , operator )
1284
+ require .NoError (t , err )
1285
+
1286
+ // Run reconcile
1287
+ res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1288
+ require .NoError (t , err )
1289
+ assert .Equal (t , ctrl.Result {}, res )
1290
+
1291
+ // Refresh the operator after reconcile
1292
+ err = cl .Get (ctx , opKey , operator )
1293
+ require .NoError (t , err )
1294
+
1295
+ // Checking the status fields
1296
+ assert .
Equal (
t ,
"quay.io/operatorhubio/[email protected] " ,
operator .
Status .
ResolvedBundleResource )
1297
+
1298
+ // checking the expected conditions
1299
+ cond := apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1300
+ require .NotNil (t , cond )
1301
+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
1302
+ assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1303
+ assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1304
+
1305
+ // We can go to the next major version when using semver
1306
+ // as well as to the version which is not next in the channel
1307
+ // when using legacy constraints
1308
+ operator .Spec .Version = "2.0.0"
1309
+ err = cl .Update (ctx , operator )
1310
+ require .NoError (t , err )
1311
+
1312
+ // Run reconcile again
1313
+ res , err = reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1314
+ require .NoError (t , err )
1315
+ assert .Equal (t , ctrl.Result {}, res )
1316
+
1317
+ // Refresh the operator after reconcile
1318
+ err = cl .Get (ctx , opKey , operator )
1319
+ require .NoError (t , err )
1320
+
1321
+ // Checking the status fields
1322
+ assert .
Equal (
t ,
"quay.io/operatorhubio/[email protected] " ,
operator .
Status .
ResolvedBundleResource )
1323
+
1324
+ // checking the expected conditions
1325
+ cond = apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1326
+ require .NotNil (t , cond )
1327
+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
1328
+ assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1329
+ assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1330
+ })
1331
+ }
1332
+ })
1333
+ }
1334
+
1335
+ func TestOperatorDowngrade (t * testing.T ) {
1336
+ ctx := context .Background ()
1337
+ fakeCatalogClient := testutil .NewFakeCatalogClient (testBundleList )
1338
+ reconciler := & controllers.OperatorReconciler {
1339
+ Client : cl ,
1340
+ Scheme : sch ,
1341
+ Resolver : solver .NewDeppySolver (controllers .NewVariableSource (cl , & fakeCatalogClient )),
1342
+ }
1343
+
1344
+ t .Run ("enforce upgrade constraints" , func (t * testing.T ) {
1345
+ for _ , tt := range []struct {
1346
+ name string
1347
+ flagState bool
1348
+ }{
1349
+ {
1350
+ name : "ForceSemverUpgradeConstraints feature gate enabled" ,
1351
+ flagState : true ,
1352
+ },
1353
+ {
1354
+ name : "ForceSemverUpgradeConstraints feature gate disabled" ,
1355
+ flagState : false ,
1356
+ },
1357
+ } {
1358
+ t .Run (tt .name , func (t * testing.T ) {
1359
+ defer featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .ForceSemverUpgradeConstraints , tt .flagState )()
1360
+ defer func () {
1361
+ require .NoError (t , cl .DeleteAllOf (ctx , & operatorsv1alpha1.Operator {}))
1362
+ require .NoError (t , cl .DeleteAllOf (ctx , & rukpakv1alpha1.BundleDeployment {}))
1363
+ }()
1364
+
1365
+ opKey := types.NamespacedName {Name : fmt .Sprintf ("operator-test-%s" , rand .String (8 ))}
1366
+ operator := & operatorsv1alpha1.Operator {
1367
+ ObjectMeta : metav1.ObjectMeta {Name : opKey .Name },
1368
+ Spec : operatorsv1alpha1.OperatorSpec {
1369
+ PackageName : "prometheus" ,
1370
+ Version : "1.0.1" ,
1371
+ Channel : "beta" ,
1372
+ },
1373
+ }
1374
+ // Create an operator
1375
+ err := cl .Create (ctx , operator )
1376
+ require .NoError (t , err )
1377
+
1378
+ // Run reconcile
1379
+ res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1380
+ require .NoError (t , err )
1381
+ assert .Equal (t , ctrl.Result {}, res )
1382
+
1383
+ // Refresh the operator after reconcile
1384
+ err = cl .Get (ctx , opKey , operator )
1385
+ require .NoError (t , err )
1386
+
1387
+ // Checking the status fields
1388
+ assert .
Equal (
t ,
"quay.io/operatorhubio/[email protected] " ,
operator .
Status .
ResolvedBundleResource )
1389
+
1390
+ // checking the expected conditions
1391
+ cond := apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1392
+ require .NotNil (t , cond )
1393
+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
1394
+ assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1395
+ assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1396
+
1397
+ // Invalid operation: can not downgrade
1398
+ operator .Spec .Version = "1.0.0"
1399
+ err = cl .Update (ctx , operator )
1400
+ require .NoError (t , err )
1401
+
1402
+ // Run reconcile again
1403
+ res , err = reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1404
+ require .Error (t , err )
1405
+ assert .Equal (t , ctrl.Result {}, res )
1406
+
1407
+ // Refresh the operator after reconcile
1408
+ err = cl .Get (ctx , opKey , operator )
1409
+ require .NoError (t , err )
1410
+
1411
+ // Checking the status fields
1412
+ // TODO: https://github.com/operator-framework/operator-controller/issues/320
1413
+ assert .Equal (t , "" , operator .Status .ResolvedBundleResource )
1414
+
1415
+ // checking the expected conditions
1416
+ cond = apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1417
+ require .NotNil (t , cond )
1418
+ assert .Equal (t , metav1 .ConditionFalse , cond .Status )
1419
+ assert .Equal (t , operatorsv1alpha1 .ReasonResolutionFailed , cond .Reason )
1420
+ assert .Contains (t , cond .Message , "constraints not satisfiable" )
1421
+ assert .Contains (t , cond .Message , "installed package prometheus requires at least one of fake-catalog-prometheus-operatorhub/prometheus/beta/1.2.0, fake-catalog-prometheus-operatorhub/prometheus/beta/1.0.1;" )
1422
+ })
1423
+ }
1424
+ })
1425
+
1426
+ t .Run ("ignore upgrade constraints" , func (t * testing.T ) {
1427
+ for _ , tt := range []struct {
1428
+ name string
1429
+ flagState bool
1430
+ }{
1431
+ {
1432
+ name : "ForceSemverUpgradeConstraints feature gate enabled" ,
1433
+ flagState : true ,
1434
+ },
1435
+ {
1436
+ name : "ForceSemverUpgradeConstraints feature gate disabled" ,
1437
+ flagState : false ,
1438
+ },
1439
+ } {
1440
+ t .Run (tt .name , func (t * testing.T ) {
1441
+ defer featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .ForceSemverUpgradeConstraints , tt .flagState )()
1442
+ defer func () {
1443
+ require .NoError (t , cl .DeleteAllOf (ctx , & operatorsv1alpha1.Operator {}))
1444
+ require .NoError (t , cl .DeleteAllOf (ctx , & rukpakv1alpha1.BundleDeployment {}))
1445
+ }()
1446
+
1447
+ opKey := types.NamespacedName {Name : fmt .Sprintf ("operator-test-%s" , rand .String (8 ))}
1448
+ operator := & operatorsv1alpha1.Operator {
1449
+ ObjectMeta : metav1.ObjectMeta {Name : opKey .Name },
1450
+ Spec : operatorsv1alpha1.OperatorSpec {
1451
+ PackageName : "prometheus" ,
1452
+ Version : "2.0.0" ,
1453
+ Channel : "beta" ,
1454
+ UpgradeConstraintPolicy : operatorsv1alpha1 .UpgradeConstraintPolicyIgnore ,
1455
+ },
1456
+ }
1457
+ // Create an operator
1458
+ err := cl .Create (ctx , operator )
1459
+ require .NoError (t , err )
1460
+
1461
+ // Run reconcile
1462
+ res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1463
+ require .NoError (t , err )
1464
+ assert .Equal (t , ctrl.Result {}, res )
1465
+
1466
+ // Refresh the operator after reconcile
1467
+ err = cl .Get (ctx , opKey , operator )
1468
+ require .NoError (t , err )
1469
+
1470
+ // Checking the status fields
1471
+ assert .
Equal (
t ,
"quay.io/operatorhubio/[email protected] " ,
operator .
Status .
ResolvedBundleResource )
1472
+
1473
+ // checking the expected conditions
1474
+ cond := apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1475
+ require .NotNil (t , cond )
1476
+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
1477
+ assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1478
+ assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1479
+
1480
+ // We downgrade
1481
+ operator .Spec .Version = "1.0.0"
1482
+ err = cl .Update (ctx , operator )
1483
+ require .NoError (t , err )
1484
+
1485
+ // Run reconcile again
1486
+ res , err = reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : opKey })
1487
+ require .NoError (t , err )
1488
+ assert .Equal (t , ctrl.Result {}, res )
1489
+
1490
+ // Refresh the operator after reconcile
1491
+ err = cl .Get (ctx , opKey , operator )
1492
+ require .NoError (t , err )
1493
+
1494
+ // Checking the status fields
1495
+ assert .
Equal (
t ,
"quay.io/operatorhubio/[email protected] " ,
operator .
Status .
ResolvedBundleResource )
1496
+
1497
+ // checking the expected conditions
1498
+ cond = apimeta .FindStatusCondition (operator .Status .Conditions , operatorsv1alpha1 .TypeResolved )
1499
+ require .NotNil (t , cond )
1500
+ assert .Equal (t , metav1 .ConditionTrue , cond .Status )
1501
+ assert .Equal (t , operatorsv1alpha1 .ReasonSuccess , cond .Reason )
1502
+ assert .
Equal (
t ,
`resolved to "quay.io/operatorhubio/[email protected] "` ,
cond .
Message )
1503
+ })
1504
+ }
1505
+ })
1250
1506
}
1251
1507
1252
1508
var (
0 commit comments