@@ -25,6 +25,7 @@ class _ConfigurationProfile:
2525 version : str = ""
2626 component : str = ""
2727 region : str = ""
28+ ikey : str = ""
2829
2930 @classmethod
3031 def fill (cls , ** kwargs ) -> None :
@@ -41,6 +42,8 @@ def fill(cls, **kwargs) -> None:
4142 cls .attach = kwargs ["attach" ]
4243 if "region" in kwargs and cls .region == "" :
4344 cls .region = kwargs ["region" ]
45+ if "ikey" in kwargs and cls .ikey == "" :
46+ cls .ikey = kwargs ["ikey" ]
4447
4548
4649class OneSettingsResponse :
@@ -218,48 +221,34 @@ def evaluate_feature(feature_key: str, settings: Dict[str, Any]) -> Optional[boo
218221
219222 Example settings structure:
220223 {
221- "live_metrics ": {
224+ "FEATURE_LIVE_METRICS ": {
222225 "default": "disabled", # Feature is disabled by default
223226 "override": [
224- {"os": "w"}, # Enable on Windows (any version)
225- {"os": "l", "ver": {"min": "1.0.0b20"}}, # Enable on Linux with version >= 1.0.0b20
226- {"component": "ext", "rp": "f"} # Enable if component is exporter AND rp is functions
227+ {"os": "windows"}, # Enable on Windows (any version)
228+ # Enable on Linux at exact version 1.0.0b21 with the distro component
229+ {"os": "linux", "ver": "1.0.0b21", "component": "dst"},
230+ {"ikey": "12345678-1234-1234-1234-123456789abc"}, # Enable for a specific instrumentation key
231+ {"component": "dst"} # Enable if component is distro
227232 ]
228233 },
229- "sampling ": {
234+ "FEATURE_SDK_STATS ": {
230235 "default": "enabled", # Feature is enabled by default
231236 "override": [
232- {"os": ["w", "l"]}, # Disable on Windows OR Linux
233- {"ver": {"max": "1.0.0"}}, # Disable on versions <= 1.0.0
234- # Disable if attach is integratedauto/manual AND region is eastus
235- {"attach": ["i", "m"], "region": "eastus"}
236- ]
237- },
238- "profiling": {
239- "default": "disabled",
240- "override": [
241- {"os": "w", "ver": {"min": "2.0.0", "max": "3.0.0"}}, # Enable on Windows with version 2.0.0-3.0.0
242- # Enable if component is exporter AND rp is functions/appsvc AND region is westus/eastus
243- {"component": "ext", "rp": ["f", "a"], "region": ["westus", "eastus"]}
244- ]
245- },
246- "debug_logging": {
247- "default": "enabled",
248- "override": [
249- {"ver": "1.0.0b1"}, # Disable on exact version 1.0.0b1
250- # Disable on Linux with distro component, manual attach, and AKS runtime
251- {"os": "l", "component": "dst", "attach": "m", "rp": "k"}
237+ {"os": "linux"}, # Disable on Linux
238+ # Disable on Linux at exact version 1.0.0b20 with the exporter component
239+ {"os": "linux", "ver": "1.0.0b20", "component": "ext"}
252240 ]
253241 }
254242 }
255243
256- Available condition fields:
257- - os: Operating system ("w"=windows, "l"=linux, "d"=darwin, "u"=unknown, etc.) - supports single value or list
258- - ver: Version constraints - supports exact string match or dict with "min"/"max" keys
259- - component: Component type ("ext"=exporter, "dst"=distro) - exact string match
260- - rp: Runtime platform ("u"=unknown, "f"=functions, "a"=appsvc, "k"=aks) - supports single value or list
261- - region: Host region ("westus", "eastus", etc.) - supports single value or list
262- - attach: Attachment type ("m"=manual, "i"=integratedauto) - supports single value or list
244+ Available condition fields (each override rule is a single-value exact match per field):
245+ - os: Operating system ("windows", "linux", "darwin", "unknown")
246+ - ver: Exact version string match (e.g. "1.0.0b21"); when present, "component" is also required
247+ - rp: Resource provider ("appsvc", "fn", "aks", "unknown")
248+ - ikey: Instrumentation key (GUID format, case-insensitive)
249+ - component: Component type ("dst"=distro, "ext"=exporter, "mot"=msft distro)
250+ - attach: Attach type ("manual", "integratedauto")
251+ - region: Azure region (e.g. "eastus", "westeurope")
263252
264253 Override logic:
265254 - Each item in the override list is an independent rule
@@ -301,6 +290,11 @@ def _matches_override_rule(override_rule: Dict[str, Any]) -> bool:
301290
302291 All conditions within a single override rule must match for the rule to apply.
303292
293+ A version ("ver") condition is only honored when the rule also carries a "component"
294+ condition (per the OneSettings schema, "ver" requires "component"). A rule that specifies
295+ "ver" without "component" is treated as non-matching. Because "component" is a regular
296+ condition, it is still matched against the current profile like any other field.
297+
304298 :param override_rule: Dictionary of conditions that must all be true
305299 :type override_rule: Dict[str, Any]
306300 :return: True if all conditions in the rule match, False otherwise
@@ -310,6 +304,10 @@ def _matches_override_rule(override_rule: Dict[str, Any]) -> bool:
310304 if not override_rule :
311305 return False
312306
307+ # A "ver" condition requires a "component" condition to be present in the same rule.
308+ if "ver" in override_rule and "component" not in override_rule :
309+ return False
310+
313311 # All conditions in this rule must match
314312 for condition_key , condition_value in override_rule .items ():
315313 if not _matches_condition (condition_key , condition_value ):
@@ -338,129 +336,35 @@ def _matches_condition(condition_key: str, condition_value: Any) -> bool:
338336 return False
339337
340338 if condition_key == "os" :
341- # OS condition - check if current OS is in the list
342- if isinstance (condition_value , list ):
343- return profile .os .lower () in [str (os ).lower () for os in condition_value ]
339+ # OS condition - exact match (case-insensitive)
344340 return profile .os .lower () == str (condition_value ).lower ()
345341
346342 if condition_key == "ver" :
347- # Version condition - support min/max version checks
348- if isinstance (condition_value , dict ):
349- current_version = profile .version
350- if not current_version :
351- return False
352-
353- # Check minimum version
354- if "min" in condition_value :
355- min_version = condition_value ["min" ]
356- if not _compare_versions (current_version , str (min_version ), ">=" ):
357- return False
358-
359- # Check maximum version
360- if "max" in condition_value :
361- max_version = condition_value ["max" ]
362- if not _compare_versions (current_version , str (max_version ), "<=" ):
363- return False
364-
365- return True
366- # Exact version match
343+ # Version condition - exact match
367344 return profile .version == str (condition_value )
368345
369346 if condition_key == "component" :
370347 # Component condition - exact match
371348 return profile .component == str (condition_value )
372349
373350 if condition_key == "rp" :
374- # Runtime platform condition - check if current RP is in the list
375- if isinstance (condition_value , list ):
376- return profile .rp in [str (rp ) for rp in condition_value ]
351+ # Resource provider condition - exact match
377352 return profile .rp == str (condition_value )
378353
379354 if condition_key == "region" :
380- # Region condition - check if current region is in the list
381- if isinstance (condition_value , list ):
382- return profile .region in [str (region ) for region in condition_value ]
355+ # Region condition - exact match
383356 return profile .region == str (condition_value )
384357
385358 if condition_key == "attach" :
386- # Attach type condition - check if current attach type is in the list
387- if isinstance (condition_value , list ):
388- return profile .attach in [str (attach ) for attach in condition_value ]
359+ # Attach type condition - exact match
389360 return profile .attach == str (condition_value )
390361
362+ if condition_key == "ikey" :
363+ # Instrumentation key condition - exact match, case-insensitive (GUIDs are hex)
364+ return profile .ikey .lower () == str (condition_value ).lower ()
365+
391366 # Unknown condition key
392367 return False
393368
394369
395- def _compare_versions (version1 : str , version2 : str , operator : str ) -> bool :
396- """Compare two version strings using the specified operator.
397-
398- Handles standard semantic versioning with beta versions (e.g., "1.0.0b28").
399-
400- :param version1: First version string (e.g., "2.9.1", "1.0.0b28")
401- :type version1: str
402- :param version2: Second version string (e.g., "2.9.0", "1.0.0b20")
403- :type version2: str
404- :param operator: Comparison operator (">=", "<=", "==", ">", "<")
405- :type operator: str
406- :return: True if the comparison is satisfied, False otherwise
407- :rtype: bool
408- """
409- try :
410- # Parse version strings into comparable tuples
411- v1_parts = _parse_version_with_beta (version1 )
412- v2_parts = _parse_version_with_beta (version2 )
413-
414- # Compare tuples
415- if operator == ">=" :
416- return v1_parts >= v2_parts
417- if operator == "<=" :
418- return v1_parts <= v2_parts
419- if operator == "==" :
420- return v1_parts == v2_parts
421- if operator == ">" :
422- return v1_parts > v2_parts
423- if operator == "<" :
424- return v1_parts < v2_parts
425- return False
426- except (ValueError , AttributeError ):
427- # If version parsing fails, fall back to string comparison
428- if operator == ">=" :
429- return version1 >= version2
430- if operator == "<=" :
431- return version1 <= version2
432- if operator == "==" :
433- return version1 == version2
434- if operator == ">" :
435- return version1 > version2
436- if operator == "<" :
437- return version1 < version2
438- return False
439-
440-
441- def _parse_version_with_beta (version : str ) -> tuple :
442- """Parse a version string that may contain beta suffix into a comparable tuple.
443-
444- Examples:
445- - "1.0.0" -> (1, 0, 0, float('inf')) # Release version sorts after beta
446- - "1.0.0b28" -> (1, 0, 0, 28) # Beta version with number
447- - "2.1.5b1" -> (2, 1, 5, 1) # Beta version with number
448-
449- :param version: Version string to parse
450- :type version: str
451- :return: Tuple representing version for comparison
452- :rtype: tuple
453- """
454- # Check if version contains beta suffix
455- if "b" in version :
456- # Split on 'b' to separate base version and beta number
457- base_version , beta_part = version .split ("b" , 1 )
458- base_parts = [int (x ) for x in base_version .split ("." )]
459- beta_number = int (beta_part ) if beta_part .isdigit () else 0
460- return tuple (base_parts + [beta_number ])
461- # Release version - use infinity for beta part so it sorts after beta versions
462- base_parts = [int (x ) for x in version .split ("." )]
463- return tuple (base_parts + [float ("inf" )])
464-
465-
466370# cSpell:enable
0 commit comments