@@ -12,6 +12,7 @@ use nexus_test_utils::resource_helpers::create_local_user;
12
12
use nexus_test_utils:: resource_helpers:: grant_iam;
13
13
use nexus_test_utils:: resource_helpers:: link_ip_pool;
14
14
use nexus_test_utils:: resource_helpers:: object_create;
15
+ use nexus_test_utils:: resource_helpers:: object_create_error;
15
16
use nexus_test_utils:: resource_helpers:: test_params;
16
17
use nexus_test_utils_macros:: nexus_test;
17
18
use nexus_types:: external_api:: params;
@@ -50,6 +51,27 @@ impl ResourceAllocator {
50
51
. await
51
52
}
52
53
54
+ async fn set_quotas_expect_error (
55
+ & self ,
56
+ client : & ClientTestContext ,
57
+ quotas : params:: SiloQuotasUpdate ,
58
+ code : http:: StatusCode ,
59
+ ) -> HttpErrorResponseBody {
60
+ NexusRequest :: expect_failure_with_body (
61
+ client,
62
+ code,
63
+ http:: Method :: PUT ,
64
+ "/v1/system/silos/quota-test-silo/quotas" ,
65
+ & Some ( & quotas) ,
66
+ )
67
+ . authn_as ( self . auth . clone ( ) )
68
+ . execute ( )
69
+ . await
70
+ . expect ( "Expected failure updating quotas" )
71
+ . parsed_body :: < HttpErrorResponseBody > ( )
72
+ . expect ( "Failed to read response after setting quotas" )
73
+ }
74
+
53
75
async fn get_quotas ( & self , client : & ClientTestContext ) -> SiloQuotas {
54
76
NexusRequest :: object_get (
55
77
client,
@@ -386,3 +408,68 @@ async fn test_quota_limits(cptestctx: &ControlPlaneTestContext) {
386
408
assert_eq ! ( quotas. limits. storage, quota_limit. storage. unwrap( ) ) ;
387
409
}
388
410
}
411
+
412
+ #[ nexus_test]
413
+ async fn test_negative_quota ( cptestctx : & ControlPlaneTestContext ) {
414
+ let client = & cptestctx. external_client ;
415
+
416
+ // Can't make a silo with a negative quota
417
+ let mut quotas = params:: SiloQuotasCreate :: empty ( ) ;
418
+ quotas. cpus = -1 ;
419
+ let response = object_create_error (
420
+ client,
421
+ "/v1/system/silos" ,
422
+ & params:: SiloCreate {
423
+ identity : IdentityMetadataCreateParams {
424
+ name : "negative-cpus-not-allowed" . parse ( ) . unwrap ( ) ,
425
+ description : "" . into ( ) ,
426
+ } ,
427
+ quotas,
428
+ discoverable : true ,
429
+ identity_mode : shared:: SiloIdentityMode :: LocalOnly ,
430
+ admin_group_name : None ,
431
+ tls_certificates : vec ! [ ] ,
432
+ mapped_fleet_roles : Default :: default ( ) ,
433
+ } ,
434
+ http:: StatusCode :: BAD_REQUEST ,
435
+ )
436
+ . await ;
437
+
438
+ assert ! (
439
+ response. message. contains(
440
+ "Cannot create silo quota: CPU quota must not be negative"
441
+ ) ,
442
+ "Unexpected response: {}" ,
443
+ response. message
444
+ ) ;
445
+
446
+ // Make the silo with an empty quota
447
+ let system = setup_silo_with_quota (
448
+ & client,
449
+ "quota-test-silo" ,
450
+ params:: SiloQuotasCreate :: empty ( ) ,
451
+ )
452
+ . await ;
453
+
454
+ // Can't update a silo with a negative quota
455
+ let quota_limit = params:: SiloQuotasUpdate {
456
+ cpus : Some ( -1 ) ,
457
+ memory : Some ( 0_u64 . try_into ( ) . unwrap ( ) ) ,
458
+ storage : Some ( 0_u64 . try_into ( ) . unwrap ( ) ) ,
459
+ } ;
460
+ let response = system
461
+ . set_quotas_expect_error (
462
+ client,
463
+ quota_limit. clone ( ) ,
464
+ http:: StatusCode :: BAD_REQUEST ,
465
+ )
466
+ . await ;
467
+
468
+ assert ! (
469
+ response. message. contains(
470
+ "Cannot update silo quota: CPU quota must not be negative"
471
+ ) ,
472
+ "Unexpected response: {}" ,
473
+ response. message
474
+ ) ;
475
+ }
0 commit comments