11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- use crate :: discovery:: discovery_trait:: { DiscoveryFilter , DiscoveryKind , ResourceDiscovery } ;
4+ use crate :: discovery:: {
5+ discovery_trait:: { DiscoveryFilter , DiscoveryKind , ResourceDiscovery }
6+ } ;
7+ use crate :: { locked_is_empty, locked_extend, locked_clone, locked_get} ;
58use crate :: dscresources:: dscresource:: { Capability , DscResource , ImplementedAs } ;
69use crate :: dscresources:: resource_manifest:: { import_manifest, validate_semver, Kind , ResourceManifest , SchemaKind } ;
710use crate :: dscresources:: command_resource:: invoke_command;
@@ -15,7 +18,7 @@ use rust_i18n::t;
1518use semver:: { Version , VersionReq } ;
1619use schemars:: JsonSchema ;
1720use serde:: { Deserialize , Serialize } ;
18- use std:: collections:: { BTreeMap , HashSet , HashMap } ;
21+ use std:: { collections:: { BTreeMap , HashMap , HashSet } , sync :: { LazyLock , RwLock } } ;
1922use std:: env;
2023use std:: ffi:: OsStr ;
2124use std:: fs;
@@ -30,19 +33,21 @@ use crate::util::get_exe_path;
3033const DSC_RESOURCE_EXTENSIONS : [ & str ; 3 ] = [ ".dsc.resource.json" , ".dsc.resource.yaml" , ".dsc.resource.yml" ] ;
3134const DSC_EXTENSION_EXTENSIONS : [ & str ; 3 ] = [ ".dsc.extension.json" , ".dsc.extension.yaml" , ".dsc.extension.yml" ] ;
3235
36+ // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version
37+ static ADAPTERS : LazyLock < RwLock < BTreeMap < String , Vec < DscResource > > > > = LazyLock :: new ( || RwLock :: new ( BTreeMap :: new ( ) ) ) ;
38+ static RESOURCES : LazyLock < RwLock < BTreeMap < String , Vec < DscResource > > > > = LazyLock :: new ( || RwLock :: new ( BTreeMap :: new ( ) ) ) ;
39+ static EXTENSIONS : LazyLock < RwLock < BTreeMap < String , DscExtension > > > = LazyLock :: new ( || RwLock :: new ( BTreeMap :: new ( ) ) ) ;
40+ static ADAPTED_RESOURCES : LazyLock < RwLock < BTreeMap < String , Vec < DscResource > > > > = LazyLock :: new ( || RwLock :: new ( BTreeMap :: new ( ) ) ) ;
41+
3342#[ derive( Clone , Serialize , Deserialize , JsonSchema ) ]
3443pub enum ImportedManifest {
3544 Resource ( DscResource ) ,
3645 Extension ( DscExtension ) ,
3746}
3847
48+
3949#[ derive( Clone ) ]
4050pub struct CommandDiscovery {
41- // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version
42- adapters : BTreeMap < String , Vec < DscResource > > ,
43- resources : BTreeMap < String , Vec < DscResource > > ,
44- extensions : BTreeMap < String , DscExtension > ,
45- adapted_resources : BTreeMap < String , Vec < DscResource > > ,
4651 progress_format : ProgressFormat ,
4752}
4853
@@ -72,18 +77,12 @@ impl CommandDiscovery {
7277 #[ must_use]
7378 pub fn new ( progress_format : ProgressFormat ) -> CommandDiscovery {
7479 CommandDiscovery {
75- adapters : BTreeMap :: new ( ) ,
76- resources : BTreeMap :: new ( ) ,
77- extensions : BTreeMap :: new ( ) ,
78- adapted_resources : BTreeMap :: new ( ) ,
7980 progress_format,
8081 }
8182 }
8283
8384 #[ must_use]
84- pub fn get_extensions ( & self ) -> & BTreeMap < String , DscExtension > {
85- & self . extensions
86- }
85+ pub fn get_extensions ( & self ) -> BTreeMap < String , DscExtension > { locked_clone ! ( EXTENSIONS ) }
8786
8887 fn get_resource_path_setting ( ) -> Result < ResourcePathSetting , DscError >
8988 {
@@ -307,7 +306,7 @@ impl ResourceDiscovery for CommandDiscovery {
307306 match kind {
308307 DiscoveryKind :: Resource => {
309308 // Now we need to call discover extensions and add those resource to the list of resources
310- for extension in self . extensions . values ( ) {
309+ for extension in locked_clone ! ( EXTENSIONS ) . values ( ) {
311310 if extension. capabilities . contains ( & ExtensionCapability :: Discover ) {
312311 debug ! ( "{}" , t!( "discovery.commandDiscovery.callingExtension" , extension = extension. type_name) ) ;
313312 let discovered_resources = match extension. discover ( ) {
@@ -326,26 +325,27 @@ impl ResourceDiscovery for CommandDiscovery {
326325 }
327326 }
328327 }
329- self . adapters = adapters;
330- self . resources = resources;
328+ locked_extend ! ( ADAPTERS , adapters) ;
329+ locked_extend ! ( RESOURCES , resources) ;
331330 } ,
332331 DiscoveryKind :: Extension => {
333- self . extensions = extensions;
332+ locked_extend ! ( EXTENSIONS , extensions) ;
334333 }
335334 }
336335
337336 Ok ( ( ) )
338337 }
339338
340339 fn discover_adapted_resources ( & mut self , name_filter : & str , adapter_filter : & str ) -> Result < ( ) , DscError > {
341- if self . resources . is_empty ( ) && self . adapters . is_empty ( ) {
340+ if locked_is_empty ! ( RESOURCES ) && locked_is_empty ! ( ADAPTERS ) {
342341 self . discover ( & DiscoveryKind :: Resource , "*" ) ?;
343342 }
344343
345- if self . adapters . is_empty ( ) {
344+ if locked_is_empty ! ( ADAPTERS ) {
346345 return Ok ( ( ) ) ;
347346 }
348347
348+ let adapters = locked_clone ! ( ADAPTERS ) ;
349349 let regex_str = convert_wildcard_to_regex ( adapter_filter) ;
350350 debug ! ( "Using regex {regex_str} as filter for adapter name" ) ;
351351 let mut regex_builder = RegexBuilder :: new ( & regex_str) ;
@@ -362,13 +362,13 @@ impl ResourceDiscovery for CommandDiscovery {
362362 return Err ( DscError :: Operation ( "Could not build Regex filter for resource name" . to_string ( ) ) ) ;
363363 } ;
364364
365- let mut progress = ProgressBar :: new ( self . adapters . len ( ) as u64 , self . progress_format ) ?;
365+ let mut progress = ProgressBar :: new ( adapters. len ( ) as u64 , self . progress_format ) ?;
366366 progress. write_activity ( "Searching for adapted resources" ) ;
367367
368368 let mut adapted_resources = BTreeMap :: < String , Vec < DscResource > > :: new ( ) ;
369369
370370 let mut found_adapter: bool = false ;
371- for ( adapter_name, adapters) in & self . adapters {
371+ for ( adapter_name, adapters) in & adapters {
372372 for adapter in adapters {
373373 progress. write_increment ( 1 ) ;
374374
@@ -437,7 +437,7 @@ impl ResourceDiscovery for CommandDiscovery {
437437 return Err ( DscError :: AdapterNotFound ( adapter_filter. to_string ( ) ) ) ;
438438 }
439439
440- self . adapted_resources = adapted_resources;
440+ locked_extend ! ( ADAPTED_RESOURCES , adapted_resources) ;
441441
442442 Ok ( ( ) )
443443 }
@@ -447,26 +447,27 @@ impl ResourceDiscovery for CommandDiscovery {
447447 if * kind == DiscoveryKind :: Resource {
448448 if adapter_name_filter. is_empty ( ) {
449449 self . discover ( kind, type_name_filter) ?;
450- for ( resource_name, resources_vec) in & self . resources {
450+ for ( resource_name, resources_vec) in & locked_clone ! ( RESOURCES ) {
451451 resources. insert ( resource_name. clone ( ) , resources_vec. iter ( ) . map ( |r| ImportedManifest :: Resource ( r. clone ( ) ) ) . collect ( ) ) ;
452452 }
453- for ( adapter_name, adapter_vec) in & self . adapters {
453+ for ( adapter_name, adapter_vec) in & locked_clone ! ( ADAPTERS ) {
454454 resources. insert ( adapter_name. clone ( ) , adapter_vec. iter ( ) . map ( |r| ImportedManifest :: Resource ( r. clone ( ) ) ) . collect ( ) ) ;
455455 }
456456 } else {
457457 self . discover ( kind, "*" ) ?;
458458 self . discover_adapted_resources ( type_name_filter, adapter_name_filter) ?;
459459
460460 // add/update found adapted resources to the lookup_table
461- add_resources_to_lookup_table ( & self . adapted_resources ) ;
461+ let adapted_resources = locked_clone ! ( ADAPTED_RESOURCES ) ;
462+ add_resources_to_lookup_table ( & adapted_resources) ;
462463
463- for ( adapted_name, adapted_vec) in & self . adapted_resources {
464+ for ( adapted_name, adapted_vec) in & adapted_resources {
464465 resources. insert ( adapted_name. clone ( ) , adapted_vec. iter ( ) . map ( |r| ImportedManifest :: Resource ( r. clone ( ) ) ) . collect ( ) ) ;
465466 }
466467 }
467468 } else {
468469 self . discover ( kind, type_name_filter) ?;
469- for ( extension_name, extension) in & self . extensions {
470+ for ( extension_name, extension) in & locked_clone ! ( EXTENSIONS ) {
470471 resources. insert ( extension_name. clone ( ) , vec ! [ ImportedManifest :: Extension ( extension. clone( ) ) ] ) ;
471472 }
472473 }
@@ -476,16 +477,18 @@ impl ResourceDiscovery for CommandDiscovery {
476477
477478 fn find_resources ( & mut self , required_resource_types : & [ DiscoveryFilter ] ) -> Result < BTreeMap < String , Vec < DscResource > > , DscError > {
478479 debug ! ( "{}" , t!( "discovery.commandDiscovery.searchingForResources" , resources = required_resource_types : { : ?} ) ) ;
479- self . discover ( & DiscoveryKind :: Resource , "*" ) ?;
480+ if locked_is_empty ! ( RESOURCES ) {
481+ self . discover ( & DiscoveryKind :: Resource , "*" ) ?;
482+ }
480483 let mut found_resources = BTreeMap :: < String , Vec < DscResource > > :: new ( ) ;
481484 let mut required_resources = HashMap :: < DiscoveryFilter , bool > :: new ( ) ;
482485 for filter in required_resource_types {
483486 required_resources. insert ( filter. clone ( ) , false ) ;
484487 }
485488
486489 for filter in required_resource_types {
487- if let Some ( resources) = self . resources . get ( filter. resource_type ( ) ) {
488- filter_resources ( & mut found_resources, & mut required_resources, resources, filter) ;
490+ if let Some ( resources) = locked_get ! ( RESOURCES , filter. resource_type( ) ) {
491+ filter_resources ( & mut found_resources, & mut required_resources, & resources, filter) ;
489492 }
490493 if required_resources. values ( ) . all ( |& v| v) {
491494 break ;
@@ -498,12 +501,12 @@ impl ResourceDiscovery for CommandDiscovery {
498501 }
499502
500503 // now go through the adapters, this is for implicit adapters so version can't be specified so use latest version
501- for adapter_name in self . adapters . clone ( ) . keys ( ) {
504+ for adapter_name in locked_clone ! ( ADAPTERS ) . keys ( ) {
502505 self . discover_adapted_resources ( "*" , adapter_name) ?;
503- add_resources_to_lookup_table ( & self . adapted_resources ) ;
506+ add_resources_to_lookup_table ( & locked_clone ! ( ADAPTED_RESOURCES ) ) ;
504507 for filter in required_resource_types {
505- if let Some ( adapted_resources) = self . adapted_resources . get ( filter. resource_type ( ) ) {
506- filter_resources ( & mut found_resources, & mut required_resources, adapted_resources, filter) ;
508+ if let Some ( adapted_resources) = locked_get ! ( ADAPTED_RESOURCES , filter. resource_type( ) ) {
509+ filter_resources ( & mut found_resources, & mut required_resources, & adapted_resources, filter) ;
507510 }
508511 if required_resources. values ( ) . all ( |& v| v) {
509512 break ;
@@ -518,10 +521,10 @@ impl ResourceDiscovery for CommandDiscovery {
518521 }
519522
520523 fn get_extensions ( & mut self ) -> Result < BTreeMap < String , DscExtension > , DscError > {
521- if self . extensions . is_empty ( ) {
524+ if locked_is_empty ! ( EXTENSIONS ) {
522525 self . discover ( & DiscoveryKind :: Extension , "*" ) ?;
523526 }
524- Ok ( self . extensions . clone ( ) )
527+ Ok ( locked_clone ! ( EXTENSIONS ) )
525528 }
526529}
527530
0 commit comments