@@ -122,17 +122,10 @@ def _fleet_files_payload():
122122 return [{"name" : PARSER_FILE , "contents" : encoded }]
123123
124124
125- def _bootstrap_config (cache_dir , api_port , * , legacy = False , interval_sec = "3600" ):
125+ def _customs_section (cache_dir , api_port , * , legacy = False , interval_sec = "3600" ):
126126 legacy_value = "on" if legacy else "off"
127127
128128 return f"""
129- service:
130- flush: 1
131- grace: 1
132- log_level: info
133- http_server: on
134- http_port: ${{FLUENT_BIT_HTTP_MONITORING_PORT}}
135-
136129customs:
137130 - name: calyptia
138131 api_key: { API_KEY }
@@ -149,24 +142,22 @@ def _bootstrap_config(cache_dir, api_port, *, legacy=False, interval_sec="3600")
149142""" .lstrip ()
150143
151144
152- def _custom_config (cache_dir , api_port , * , legacy = False , interval_sec = "3600" ):
153- legacy_value = "on" if legacy else "off"
154-
145+ def _bootstrap_config (cache_dir , api_port , * , legacy = False , interval_sec = "3600" ):
155146 return f"""
156- customs :
157- - name: calyptia
158- api_key: { API_KEY }
159- calyptia_host: 127.0.0.1
160- calyptia_port: " { api_port } "
161- calyptia_tls: off
162- calyptia_tls.verify: off
163- fleet_name: { FLEET_ID }
164- machine_id: { MACHINE_ID }
165- fleet.config_dir: { _yaml_string ( cache_dir ) }
166- fleet_config_legacy_format: { legacy_value }
167- fleet.interval_sec: " { interval_sec } "
168- fleet.interval_nsec: "0"
169- """ . lstrip ( )
147+ service :
148+ flush: 1
149+ grace: 1
150+ log_level: info
151+ http_server: on
152+ http_port: ${{FLUENT_BIT_HTTP_MONITORING_PORT}}
153+
154+ """ . lstrip () + _customs_section (
155+ cache_dir , api_port , legacy = legacy , interval_sec = interval_sec
156+ )
157+
158+
159+ def _custom_config ( cache_dir , api_port , * , legacy = False , interval_sec = "3600" ):
160+ return _customs_section ( cache_dir , api_port , legacy = legacy , interval_sec = interval_sec )
170161
171162
172163def _fleet_config (
@@ -237,7 +228,15 @@ def _write_bootstrap_config(tmp_path, cache_dir, api_port, *, legacy=False, inte
237228 return config_path
238229
239230
240- def _write_old_cache (cache_dir , api_port , ref_name , marker , * , target_marker = None ):
231+ def _write_old_cache (
232+ cache_dir ,
233+ api_port ,
234+ ref_name ,
235+ marker ,
236+ * ,
237+ target_marker = None ,
238+ interval_sec = "3600" ,
239+ ):
241240 timestamp = 1234567890
242241 base_dir = _fleet_base_dir (cache_dir )
243242 timestamp_dir = base_dir / str (timestamp )
@@ -247,14 +246,26 @@ def _write_old_cache(cache_dir, api_port, ref_name, marker, *, target_marker=Non
247246
248247 timestamp_dir .mkdir (parents = True )
249248 flat_config .write_text (
250- _fleet_config (cache_dir , api_port , marker , include_custom = True ),
249+ _fleet_config (
250+ cache_dir ,
251+ api_port ,
252+ marker ,
253+ include_custom = True ,
254+ interval_sec = interval_sec ,
255+ ),
251256 encoding = "utf-8" ,
252257 )
253258 parser_config .write_text (PARSER_CONFIG , encoding = "utf-8" )
254259
255260 if target_marker is not None :
256261 nested_config .write_text (
257- _fleet_config (cache_dir , api_port , target_marker , include_custom = True ),
262+ _fleet_config (
263+ cache_dir ,
264+ api_port ,
265+ target_marker ,
266+ include_custom = True ,
267+ interval_sec = interval_sec ,
268+ ),
258269 encoding = "utf-8" ,
259270 )
260271
@@ -529,6 +540,54 @@ def test_fleet_startup_migrates_flat_cur_ref_for_relative_includes(tmp_path):
529540 assert marker in log_text
530541
531542
543+ def test_fleet_migrates_flat_cache_then_applies_server_update (tmp_path ):
544+ api_port = find_available_port ()
545+ cache_dir = tmp_path / "fleet-cache"
546+ old_marker = "migrated-flat-fleet-config-ok"
547+ new_marker = "server-updated-nested-fleet-config-ok"
548+ paths = _write_old_cache (
549+ cache_dir ,
550+ api_port ,
551+ "cur" ,
552+ old_marker ,
553+ interval_sec = "2" ,
554+ )
555+ config_path = _write_bootstrap_config (tmp_path , cache_dir , api_port )
556+ fleet_config = _fleet_config (cache_dir , api_port , new_marker , interval_sec = "1" )
557+ service = FluentBitTestService (str (config_path ))
558+
559+ with FleetAPIServer (api_port , fleet_config , _fleet_files_payload ()) as api :
560+ try :
561+ service .start ()
562+ _wait_for_log_contains (service , old_marker , timeout = 30 )
563+ log_text = _wait_for_log_contains (service , new_marker , timeout = 45 )
564+ service .wait_for_condition (
565+ lambda : (
566+ paths ["ref_file" ].is_file ()
567+ and _read_ref_path (paths ["ref_file" ]) != paths ["nested_config" ]
568+ and not (paths ["base_dir" ] / "old.ref" ).exists ()
569+ ),
570+ timeout = 45 ,
571+ interval = 0.5 ,
572+ description = "updated fleet config commit and old config cleanup" ,
573+ )
574+ finally :
575+ service .stop ()
576+
577+ current_config = _read_ref_path (paths ["ref_file" ])
578+ _assert_no_include_error (log_text )
579+ assert not paths ["flat_config" ].exists ()
580+ assert not paths ["nested_config" ].exists ()
581+ assert current_config .name == "config.yaml"
582+ assert current_config .parent .parent == paths ["base_dir" ]
583+ assert (current_config .parent / PARSER_FILE ).is_file ()
584+ assert new_marker in current_config .read_text (encoding = "utf-8" )
585+ assert any (request ["path" ] == f"/v1/fleets/{ FLEET_ID } /config" for request in api .requests )
586+ assert any (request ["path" ] == f"/v1/fleets/{ FLEET_ID } /files" for request in api .requests )
587+ assert old_marker in log_text
588+ assert new_marker in log_text
589+
590+
532591def test_fleet_startup_keeps_nested_cur_ref_for_relative_includes (tmp_path ):
533592 api_port = find_available_port ()
534593 cache_dir = tmp_path / "fleet-cache"
0 commit comments