@@ -30,12 +30,7 @@ impl Client {
3030 . await
3131 . context ( PerformRawRequestSnafu ) ?;
3232
33- response
34- . lines ( )
35- . filter ( |l| l. starts_with ( "kubernetes_feature_enabled" ) )
36- . map ( FeatureGate :: from_str)
37- . collect :: < Result < Vec < FeatureGate > , _ > > ( )
38- . map_err ( |error| ParseFeatureGateSnafu { error } . build ( ) )
33+ FeatureGate :: parse_from_metrics ( & response)
3934 }
4035
4136 /// Retrieves enabled feature gates.
@@ -94,6 +89,18 @@ impl FromStr for FeatureGate {
9489}
9590
9691impl FeatureGate {
92+ pub const METRIC_NAME : & str = "kubernetes_feature_enabled" ;
93+
94+ /// Enumerates the complete body line-by-line and parses the relevant feature gate metrics.
95+ #[ allow( clippy:: result_large_err) ]
96+ fn parse_from_metrics ( body : & str ) -> Result < Vec < Self > > {
97+ body. lines ( )
98+ . filter ( |l| l. starts_with ( Self :: METRIC_NAME ) )
99+ . map ( Self :: from_str)
100+ . collect :: < Result < Vec < Self > , _ > > ( )
101+ . map_err ( |error| ParseFeatureGateSnafu { error } . build ( ) )
102+ }
103+
97104 /// Parses a feature gate from the line-based `/metrics` response.
98105 ///
99106 /// This function expects feature gates to be passed as individual lines.
@@ -134,7 +141,7 @@ impl FeatureGate {
134141
135142 /// Parses (and removes) the well-known, static metric name.
136143 fn parse_metric_name ( input : & mut & str ) -> winnow:: Result < ( ) > {
137- "kubernetes_feature_enabled" . void ( ) . parse_next ( input)
144+ Self :: METRIC_NAME . void ( ) . parse_next ( input)
138145 }
139146
140147 /// Parses and collects a list of labels contained within `{` and `}`.
@@ -208,6 +215,7 @@ pub enum FeatureStage {
208215
209216#[ cfg( test) ]
210217mod tests {
218+ use indoc:: indoc;
211219 use rstest:: rstest;
212220
213221 use super :: * ;
@@ -230,13 +238,37 @@ mod tests {
230238 }
231239 }
232240
233- #[ rstest]
234- #[ case( r#"kubernetes_feature_enabled{name="APIResponseCompression",stage="BETA"} 1"# ) ]
235- #[ case( r#"kubernetes_feature_enabled{name="APIServingWithRoutine",stage="ALPHA"} 0"# ) ]
236- #[ case( r#"kubernetes_feature_enabled{name="AggregatedDiscoveryRemoveBetaType",stage="DEPRECATED"} 1"# ) ]
237- #[ case( r#"kubernetes_feature_enabled{name="AnyVolumeDataSource",stage=""} 1"# ) ]
238- fn parse_feature_gate_valid ( #[ case] input : & str ) {
239- assert ! ( FeatureGate :: from_str( input) . is_ok( ) )
241+ #[ test]
242+ fn parse_feature_gates ( ) {
243+ // This snippet is a combination of
244+ //
245+ // - kubectl get --raw /metrics | head
246+ // - kubectl get --raw /metrics | grep kubernetes_feature_enabled | head
247+ let response = indoc ! { r#"
248+ # HELP aggregator_discovery_aggregation_count_total [ALPHA] Counter of number of times discovery was aggregated
249+ # TYPE aggregator_discovery_aggregation_count_total counter
250+ aggregator_discovery_aggregation_count_total 614
251+ # HELP aggregator_unavailable_apiservice [ALPHA] Gauge of APIServices which are marked as unavailable broken down by APIService name.
252+ # TYPE aggregator_unavailable_apiservice gauge
253+ aggregator_unavailable_apiservice{name="v1."} 0
254+ aggregator_unavailable_apiservice{name="v1.admissionregistration.k8s.io"} 0
255+ aggregator_unavailable_apiservice{name="v1.apiextensions.k8s.io"} 0
256+ aggregator_unavailable_apiservice{name="v1.apps"} 0
257+ aggregator_unavailable_apiservice{name="v1.authentication.k8s.io"} 0
258+ # ...
259+ # HELP kubernetes_feature_enabled [BETA] This metric records the data about the stage and enablement of a k8s feature.
260+ # TYPE kubernetes_feature_enabled gauge
261+ kubernetes_feature_enabled{name="APIResponseCompression",stage="BETA"} 1
262+ kubernetes_feature_enabled{name="APIServerIdentity",stage="BETA"} 1
263+ kubernetes_feature_enabled{name="APIServerTracing",stage="BETA"} 1
264+ kubernetes_feature_enabled{name="APIServingWithRoutine",stage="ALPHA"} 0
265+ kubernetes_feature_enabled{name="AggregatedDiscoveryRemoveBetaType",stage="DEPRECATED"} 1
266+ kubernetes_feature_enabled{name="AllAlpha",stage="ALPHA"} 0
267+ kubernetes_feature_enabled{name="AllBeta",stage="BETA"} 0
268+ kubernetes_feature_enabled{name="AllowDNSOnlyNodeCSR",stage="DEPRECATED"} 0
269+ "# } ;
270+
271+ assert ! ( FeatureGate :: parse_from_metrics( response) . is_ok( ) ) ;
240272 }
241273
242274 #[ rstest]
@@ -247,6 +279,6 @@ mod tests {
247279 #[ case( "kubernetes_feature_enabled{} 0" ) ]
248280 #[ case( "" ) ]
249281 fn parse_feature_gate_invalid ( #[ case] input : & str ) {
250- assert ! ( FeatureGate :: from_str( input) . is_err( ) )
282+ assert ! ( FeatureGate :: from_str( input) . is_err( ) ) ;
251283 }
252284}
0 commit comments