1- use std:: { sync:: Arc , time:: Duration } ;
1+ use std:: { fs :: OpenOptions , sync:: Arc , time:: Duration } ;
22
33use futures:: StreamExt ;
44use gateway_api:: {
@@ -16,7 +16,11 @@ use k8s_openapi::api::{
1616 core:: v1:: Service ,
1717 networking:: v1:: { Ingress , IngressServiceBackend , ServiceBackendPort } ,
1818} ;
19- use kube:: { Api , Resource , ResourceExt , api:: PatchParams , runtime:: controller:: Action } ;
19+ use kube:: {
20+ Api , Resource , ResourceExt ,
21+ api:: { ObjectMeta , PatchParams } ,
22+ runtime:: controller:: Action ,
23+ } ;
2024use tracing:: Instrument ;
2125
2226use crate :: {
@@ -25,6 +29,7 @@ use crate::{
2529} ;
2630
2731mod args;
32+ mod consts;
2833mod ctx;
2934mod err;
3035mod utils;
@@ -61,19 +66,28 @@ async fn get_svc_port_number(
6166 return Some ( port. port ) ;
6267}
6368
64- async fn create_http_route (
69+ async fn create_http_routes (
6570 ctx : Arc < ctx:: Context > ,
6671 ingress_name : & str ,
72+ ingress_meta : & ObjectMeta ,
6773 section_name : Option < & String > ,
6874 ingress_namespace : & str ,
6975 http : & k8s_openapi:: api:: networking:: v1:: HTTPIngressRuleValue ,
7076 hostname : & str ,
71- ) -> anyhow:: Result < HTTPRoute > {
77+ ) -> anyhow:: Result < Vec < HTTPRoute > > {
7278 let safe_hostname = utils:: sanitize_hostname ( hostname) ;
7379 let gw_group = <gateways:: Gateway as kube:: Resource >:: group ( & ( ) ) ;
7480 let gw_kind = <gateways:: Gateway as kube:: Resource >:: kind ( & ( ) ) ;
7581
82+ let split_routes = ingress_meta
83+ . annotations
84+ . as_ref ( )
85+ . and_then ( |ann| ann. get ( consts:: SPLIT_ROUTES ) )
86+ . map ( |v| v. to_lowercase ( ) == "true" )
87+ . unwrap_or ( false ) ;
88+
7689 let mut rules = vec ! [ ] ;
90+
7791 for path in & http. paths {
7892 let Some ( svc) = & path. backend . service else {
7993 tracing:: warn!( "Skipping backend without service" ) ;
@@ -96,28 +110,16 @@ async fn create_http_route(
96110 ) ;
97111 continue ;
98112 } ;
99- let mut path_matches = vec ! [ ] ;
100- for path in & http. paths {
101- let match_type = match path. path_type . as_str ( ) {
102- "Prefix" => HTTPRouteRulesMatchesPathType :: PathPrefix ,
103- "Exact" => HTTPRouteRulesMatchesPathType :: Exact ,
104- "ImplementationSpecific" => HTTPRouteRulesMatchesPathType :: PathPrefix ,
105- _ => {
106- return Err (
107- anyhow:: anyhow!( "Unknown path type: {}" , path. path_type. as_str( ) ) . into ( ) ,
108- ) ;
109- }
110- } ;
111- path_matches. push ( HTTPRouteRulesMatches {
112- headers : None ,
113- method : None ,
114- query_params : None ,
115- path : Some ( HTTPRouteRulesMatchesPath {
116- r#type : Some ( match_type) ,
117- value : path. path . clone ( ) ,
118- } ) ,
119- } ) ;
120- }
113+ let match_type = match path. path_type . as_str ( ) {
114+ "Prefix" => HTTPRouteRulesMatchesPathType :: PathPrefix ,
115+ "Exact" => HTTPRouteRulesMatchesPathType :: Exact ,
116+ "ImplementationSpecific" => HTTPRouteRulesMatchesPathType :: PathPrefix ,
117+ _ => {
118+ return Err (
119+ anyhow:: anyhow!( "Unknown path type: {}" , path. path_type. as_str( ) ) . into ( ) ,
120+ ) ;
121+ }
122+ } ;
121123 rules. push ( HTTPRouteRules {
122124 name : None ,
123125 backend_refs : Some (
@@ -132,16 +134,64 @@ async fn create_http_route(
132134 } ]
133135 . to_vec ( ) ,
134136 ) ,
135- matches : Some ( path_matches) ,
137+ matches : Some ( vec ! [ HTTPRouteRulesMatches {
138+ headers: None ,
139+ method: None ,
140+ query_params: None ,
141+ path: Some ( HTTPRouteRulesMatchesPath {
142+ r#type: Some ( match_type) ,
143+ value: path. path. clone( ) ,
144+ } ) ,
145+ } ] ) ,
136146 filters : None ,
137147 timeouts : None ,
138148 } ) ;
139149 }
140150 if rules. is_empty ( ) {
141151 return Err ( anyhow:: anyhow!( "No valid paths found" ) . into ( ) ) ;
142152 }
153+
154+ // If split_routes is enabled, create a separate HTTPRoute for each rule.
155+ if split_routes {
156+ return Ok ( rules
157+ . into_iter ( )
158+ . map ( |rule| {
159+ HTTPRoute :: new (
160+ & format ! (
161+ "{ingress_name}-{safe_hostname}-{}" ,
162+ utils:: sanitize_hostname(
163+ & rule
164+ . matches
165+ . as_ref( )
166+ . and_then( |m| m. first( ) )
167+ . and_then( |mm| mm. path. as_ref( ) )
168+ . and_then( |p| p. value. clone( ) )
169+ . unwrap_or_else( || "root" . to_string( ) )
170+ )
171+ ) ,
172+ HTTPRouteSpec {
173+ hostnames : Some ( vec ! [ hostname. to_string( ) ] ) ,
174+ parent_refs : Some (
175+ [ HTTPRouteParentRefs {
176+ group : Some ( gw_group. to_string ( ) ) ,
177+ kind : Some ( gw_kind. to_string ( ) ) ,
178+ name : ctx. args . default_gateway_name . clone ( ) ,
179+ namespace : Some ( ctx. args . default_gateway_namespace . clone ( ) ) ,
180+ port : None ,
181+ section_name : section_name. cloned ( ) ,
182+ } ]
183+ . to_vec ( ) ,
184+ ) ,
185+ rules : Some ( vec ! [ rule] ) ,
186+ } ,
187+ )
188+ } )
189+ . collect ( ) ) ;
190+ }
191+
192+ // Split routes is disabled, create a single HTTPRoute with all rules.
143193 let route_name = format ! ( "{ingress_name}-{safe_hostname}-http" ) ;
144- Ok ( HTTPRoute :: new (
194+ Ok ( [ HTTPRoute :: new (
145195 & route_name,
146196 HTTPRouteSpec {
147197 hostnames : Some ( vec ! [ hostname. to_string( ) ] ) ,
@@ -159,10 +209,11 @@ async fn create_http_route(
159209 ) ,
160210 rules : Some ( rules) ,
161211 } ,
162- ) )
212+ ) ]
213+ . to_vec ( ) )
163214}
164215
165- async fn create_tcp_route (
216+ async fn create_tcp_routes (
166217 ctx : Arc < ctx:: Context > ,
167218 ingress_name : & str ,
168219 section_name : Option < & String > ,
@@ -228,7 +279,7 @@ async fn create_tcp_route(
228279
229280#[ tracing:: instrument( skip( ingress, ctx) , fields( ingress = ingress. name_any( ) , namespace = ingress. namespace( ) ) , err) ]
230281pub async fn reconcile ( ingress : Arc < Ingress > , ctx : Arc < ctx:: Context > ) -> I2GResult < Action > {
231- if ctx. is_leader . load ( std:: sync:: atomic:: Ordering :: Relaxed ) {
282+ if ! ctx. is_leader . load ( std:: sync:: atomic:: Ordering :: Relaxed ) {
232283 tracing:: debug!( "Not a leader, skipping reconciliation" ) ;
233284 return Ok ( Action :: requeue ( Duration :: from_secs ( 20 ) ) ) ;
234285 }
@@ -261,9 +312,10 @@ pub async fn reconcile(ingress: Arc<Ingress>, ctx: Arc<ctx::Context>) -> I2GResu
261312 } ;
262313
263314 if let Some ( http) = & rule. http {
264- let Ok ( mut route ) = create_http_route (
315+ let Ok ( routes ) = create_http_routes (
265316 ctx. clone ( ) ,
266317 & ingress. name_any ( ) ,
318+ & ingress. meta ( ) ,
267319 desired_section_name. as_ref ( ) ,
268320 & ingress_namespace,
269321 & http,
@@ -274,21 +326,28 @@ pub async fn reconcile(ingress: Arc<Ingress>, ctx: Arc<ctx::Context>) -> I2GResu
274326 tracing:: warn!( "Failed to create HTTPRoute for host {}" , host) ;
275327 continue ;
276328 } ;
277- if ctx. args . link_to_ingress {
278- route. meta_mut ( ) . add_owner ( ingress. as_ref ( ) ) ;
329+ let a = OpenOptions :: new ( )
330+ . create ( true )
331+ . write ( true )
332+ . open ( "shit.json" )
333+ . unwrap ( ) ;
334+ serde_json:: to_writer_pretty ( a, & routes) . unwrap ( ) ;
335+ for mut route in routes {
336+ if ctx. args . link_to_ingress {
337+ route. meta_mut ( ) . add_owner ( ingress. as_ref ( ) ) ;
338+ }
339+ Api :: < HTTPRoute > :: namespaced ( ctx. client . clone ( ) , & ingress_namespace)
340+ . patch (
341+ & route. name_any ( ) ,
342+ & PatchParams {
343+ field_manager : Some ( "ingress-to-gateway-controller" . to_string ( ) ) ,
344+ ..PatchParams :: default ( )
345+ } ,
346+ & kube:: api:: Patch :: Apply ( route) ,
347+ )
348+ . instrument ( tracing:: info_span!( "Applying generated HTTPRoute" ) )
349+ . await ?;
279350 }
280-
281- Api :: < HTTPRoute > :: namespaced ( ctx. client . clone ( ) , & ingress_namespace)
282- . patch (
283- & route. name_any ( ) ,
284- & PatchParams {
285- field_manager : Some ( "ingress-to-gateway-controller" . to_string ( ) ) ,
286- ..PatchParams :: default ( )
287- } ,
288- & kube:: api:: Patch :: Apply ( route) ,
289- )
290- . instrument ( tracing:: info_span!( "Applying generated HTTPRoute" ) )
291- . await ?;
292351 } else {
293352 if !ctx. args . experimental {
294353 tracing:: warn!(
@@ -306,7 +365,7 @@ pub async fn reconcile(ingress: Arc<Ingress>, ctx: Arc<ctx::Context>) -> I2GResu
306365 continue ;
307366 } ;
308367
309- let Ok ( mut route) = create_tcp_route (
368+ let Ok ( mut route) = create_tcp_routes (
310369 ctx. clone ( ) ,
311370 & ingress. name_any ( ) ,
312371 desired_section_name. as_ref ( ) ,
0 commit comments