@@ -393,6 +393,270 @@ module VCAP::CloudController
393393 end . to raise_error ( ManifestRouteUpdate ::InvalidRoute , /Host format is invalid/ )
394394 end
395395 end
396+
397+ context 'when route options are provided' do
398+ let! ( :domain ) { VCAP ::CloudController ::SharedDomain . make ( name : 'tomato.avocado-toast.com' ) }
399+
400+ before do
401+ VCAP ::CloudController ::FeatureFlag . make ( name : 'hash_based_routing' , enabled : true )
402+ end
403+
404+ context 'when creating a new route with loadbalancing options' do
405+ let ( :message ) do
406+ ManifestRoutesUpdateMessage . new (
407+ routes : [
408+ {
409+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
410+ options : { loadbalancing : 'round-robin' }
411+ }
412+ ]
413+ )
414+ end
415+
416+ it 'creates the route with the specified loadbalancing option' do
417+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
418+
419+ routes = app . reload . routes
420+ expect ( routes . length ) . to eq ( 1 )
421+
422+ route = routes . first
423+ expect ( route . options ) . to include ( { 'loadbalancing' => 'round-robin' } )
424+ end
425+ end
426+
427+ context 'when creating a new route with hash loadbalancing and hash_header' do
428+ let ( :message ) do
429+ ManifestRoutesUpdateMessage . new (
430+ routes : [
431+ {
432+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
433+ options : {
434+ loadbalancing : 'hash' ,
435+ hash_header : 'X-User-ID'
436+ }
437+ }
438+ ]
439+ )
440+ end
441+
442+ it 'creates the route with hash loadbalancing options' do
443+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
444+
445+ routes = app . reload . routes
446+ expect ( routes . length ) . to eq ( 1 )
447+
448+ route = routes . first
449+ expect ( route . options ) . to include ( { 'loadbalancing' => 'hash' , 'hash_header' => 'X-User-ID' } )
450+ end
451+ end
452+
453+ context 'when creating a new route with hash loadbalancing, hash_header, and hash_balance' do
454+ let ( :message ) do
455+ ManifestRoutesUpdateMessage . new (
456+ routes : [
457+ {
458+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
459+ options : {
460+ loadbalancing : 'hash' ,
461+ hash_header : 'X-Session-ID' ,
462+ hash_balance : '2.5'
463+ }
464+ }
465+ ]
466+ )
467+ end
468+
469+ it 'creates the route with all hash loadbalancing options' do
470+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
471+
472+ routes = app . reload . routes
473+ expect ( routes . length ) . to eq ( 1 )
474+
475+ route = routes . first
476+ expect ( route . options ) . to include ( { 'loadbalancing' => 'hash' , 'hash_header' => 'X-Session-ID' , 'hash_balance' => '2.5' } )
477+ end
478+ end
479+
480+ context 'when creating a new route with hash loadbalancing but missing hash_header' do
481+ let ( :message ) do
482+ ManifestRoutesUpdateMessage . new (
483+ routes : [
484+ {
485+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
486+ options : { loadbalancing : 'hash' }
487+ }
488+ ]
489+ )
490+ end
491+
492+ it 'raises an error indicating hash_header is required' do
493+ expect do
494+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
495+ end . to raise_error ( ManifestRouteUpdate ::InvalidRoute , /Hash header must be present when loadbalancing is set to hash/ )
496+ end
497+ end
498+
499+ context 'when updating an existing route with new loadbalancing options' do
500+ let! ( :route ) { Route . make ( host : 'potato' , domain : domain , path : '/some-path' , space : app . space ) }
501+ let ( :message ) do
502+ ManifestRoutesUpdateMessage . new (
503+ routes : [
504+ {
505+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
506+ options : { loadbalancing : 'least-connection' }
507+ }
508+ ]
509+ )
510+ end
511+
512+ it 'updates the existing route with the new loadbalancing option' do
513+ expect do
514+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
515+ end . not_to ( change { Route . count } )
516+
517+ route . reload
518+ expect ( route . options ) . to include ( { 'loadbalancing' => 'least-connection' } )
519+ end
520+ end
521+
522+ context 'when updating an existing route from round-robin to hash' do
523+ let! ( :route ) do
524+ Route . make (
525+ host : 'potato' ,
526+ domain : domain ,
527+ path : '/some-path' ,
528+ space : app . space ,
529+ options : { loadbalancing : 'round-robin' }
530+ )
531+ end
532+
533+ let ( :message ) do
534+ ManifestRoutesUpdateMessage . new (
535+ routes : [
536+ {
537+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
538+ options : {
539+ loadbalancing : 'hash' ,
540+ hash_header : 'X-User-ID'
541+ }
542+ }
543+ ]
544+ )
545+ end
546+
547+ it 'updates the route to hash loadbalancing with hash_header' do
548+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
549+
550+ route . reload
551+ expect ( route . options ) . to include ( { 'loadbalancing' => 'hash' , 'hash_header' => 'X-User-ID' } )
552+ end
553+ end
554+
555+ context 'when updating an existing hash route with new hash_header' do
556+ let! ( :route ) do
557+ Route . make (
558+ host : 'potato' ,
559+ domain : domain ,
560+ path : '/some-path' ,
561+ space : app . space ,
562+ options : {
563+ loadbalancing : 'hash' ,
564+ hash_header : 'X-Old-Header' ,
565+ hash_balance : '2.0'
566+ }
567+ )
568+ end
569+
570+ let ( :message ) do
571+ ManifestRoutesUpdateMessage . new (
572+ routes : [
573+ {
574+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
575+ options : { hash_header : 'X-New-Header' }
576+ }
577+ ]
578+ )
579+ end
580+
581+ it 'updates only the hash_header while keeping other options' do
582+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
583+
584+ route . reload
585+ expect ( route . options ) . to include ( { 'loadbalancing' => 'hash' , 'hash_header' => 'X-New-Header' , 'hash_balance' => '2.0' } )
586+ end
587+ end
588+
589+ context 'when updating an existing hash route with new hash_balance' do
590+ let! ( :route ) do
591+ Route . make (
592+ host : 'potato' ,
593+ domain : domain ,
594+ path : '/some-path' ,
595+ space : app . space ,
596+ options : {
597+ loadbalancing : 'hash' ,
598+ hash_header : 'X-User-ID' ,
599+ hash_balance : '2.0'
600+ }
601+ )
602+ end
603+
604+ let ( :message ) do
605+ ManifestRoutesUpdateMessage . new (
606+ routes : [
607+ {
608+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
609+ options : { hash_balance : '5.0' }
610+ }
611+ ]
612+ )
613+ end
614+
615+ it 'updates only the hash_balance while keeping other options' do
616+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
617+
618+ route . reload
619+ expect ( route . options ) . to include ( { 'loadbalancing' => 'hash' , 'hash_header' => 'X-User-ID' , 'hash_balance' => '5.0' } )
620+ end
621+ end
622+
623+ context 'when updating an existing hash route to remove loadbalancing' do
624+ let! ( :route ) do
625+ Route . make (
626+ host : 'potato' ,
627+ domain : domain ,
628+ path : '/some-path' ,
629+ space : app . space ,
630+ options : {
631+ loadbalancing : 'hash' ,
632+ hash_header : 'X-User-ID' ,
633+ hash_balance : '2.0'
634+ }
635+ )
636+ end
637+
638+ let ( :message ) do
639+ ManifestRoutesUpdateMessage . new (
640+ routes : [
641+ {
642+ route : 'http://potato.tomato.avocado-toast.com/some-path' ,
643+ options : { loadbalancing : nil }
644+ }
645+ ]
646+ )
647+ end
648+
649+ it 'removes loadbalancing and hash options' do
650+ ManifestRouteUpdate . update ( app . guid , message , user_audit_info )
651+
652+ route . reload
653+ expect ( route . options ) . to eq ( { } )
654+ expect ( route . options ) . not_to have_key ( 'loadbalancing' )
655+ expect ( route . options ) . not_to have_key ( 'hash_header' )
656+ expect ( route . options ) . not_to have_key ( 'hash_balance' )
657+ end
658+ end
659+ end
396660 end
397661 end
398662end
0 commit comments