@@ -93,49 +93,35 @@ type storeExtManager struct {
9393
9494var ss * storeExtManager
9595
96- func NewStoreExtManager (execer fakeruntime.Execer ) ExtManager {
97- if ss == nil {
98- ctx , cancel := context .WithCancel (context .Background ())
99- ss = & storeExtManager {
100- processChan : make (chan fakeruntime.Process ),
101- stopSingal : make (chan struct {}, 1 ),
102- lock : & sync.RWMutex {},
103- // AI Plugin Management initialization
104- aiPluginRegistry : make (map [string ]AIPluginInfo ),
105- aiPluginHealthMap : make (map [string ]* AIPluginHealth ),
106- healthCheckCtx : ctx ,
107- healthCheckCancel : cancel ,
108- }
109- ss .execer = execer
110- ss .socketPrefix = "unix://"
111- ss .extStatusMap = map [string ]bool {}
112- ss .processCollect ()
113- ss .WithDownloader (& nonDownloader {})
114- // Start AI plugin health monitoring
115- ss .startAIHealthMonitoring ()
116- }
117- return ss
118- }
119-
120- func NewStoreExtManagerInstance (execer fakeruntime.Execer ) ExtManager {
96+ func createStoreExtManager (execer fakeruntime.Execer ) * storeExtManager {
12197 ctx , cancel := context .WithCancel (context .Background ())
122- ss = & storeExtManager {
98+ manager : = & storeExtManager {
12399 processChan : make (chan fakeruntime.Process ),
124100 stopSingal : make (chan struct {}, 1 ),
125101 lock : & sync.RWMutex {},
126- // AI Plugin Management initialization
127102 aiPluginRegistry : make (map [string ]AIPluginInfo ),
128103 aiPluginHealthMap : make (map [string ]* AIPluginHealth ),
129104 healthCheckCtx : ctx ,
130105 healthCheckCancel : cancel ,
106+ execer : execer ,
107+ socketPrefix : "unix://" ,
108+ extStatusMap : map [string ]bool {},
109+ }
110+ manager .processCollect ()
111+ manager .WithDownloader (& nonDownloader {})
112+ manager .startAIHealthMonitoring ()
113+ return manager
114+ }
115+
116+ func NewStoreExtManager (execer fakeruntime.Execer ) ExtManager {
117+ if ss == nil {
118+ ss = createStoreExtManager (execer )
131119 }
132- ss .execer = execer
133- ss .socketPrefix = "unix://"
134- ss .extStatusMap = map [string ]bool {}
135- ss .processCollect ()
136- ss .WithDownloader (& nonDownloader {})
137- // Start AI plugin health monitoring
138- ss .startAIHealthMonitoring ()
120+ return ss
121+ }
122+
123+ func NewStoreExtManagerInstance (execer fakeruntime.Execer ) ExtManager {
124+ ss = createStoreExtManager (execer )
139125 return ss
140126}
141127
@@ -145,60 +131,75 @@ func (s *storeExtManager) StartPlugin(storeKind testing.StoreKind) {
145131 }
146132}
147133
134+ func (s * storeExtManager ) getPlatformBinaryName (name string ) string {
135+ if s .execer .OS () == "windows" {
136+ return name + ".exe"
137+ }
138+ return name
139+ }
140+
141+ func (s * storeExtManager ) resolveBinaryPath (name string ) (string , error ) {
142+ platformBasedName := s .getPlatformBinaryName (name )
143+ targetDir := home .GetUserBinDir ()
144+ targetBinaryFile := filepath .Join (targetDir , platformBasedName )
145+
146+ if _ , err := os .Stat (targetBinaryFile ); err == nil {
147+ return targetBinaryFile , nil
148+ }
149+
150+ serverLogger .Info ("failed to find extension locally, looking in PATH" , "name" , name )
151+ binaryPath , err := s .execer .LookPath (platformBasedName )
152+ if err != nil {
153+ return "" , fmt .Errorf ("not found extension, try to download it, error: %v" , err )
154+ }
155+ return binaryPath , nil
156+ }
157+
158+ func (s * storeExtManager ) downloadExtensionAsync (name , socket string ) {
159+ go func () {
160+ ociDownloader := downloader .NewStoreDownloader ()
161+ ociDownloader .WithKind ("store" )
162+ ociDownloader .WithOS (s .execer .OS ())
163+ reader , dErr := ociDownloader .Download (name , "" , "" )
164+ if dErr != nil {
165+ serverLogger .Error (dErr , "failed to download extension" , "name" , name )
166+ return
167+ }
168+
169+ extFile := ociDownloader .GetTargetFile ()
170+ targetDir := home .GetUserBinDir ()
171+ targetFile := filepath .Base (extFile )
172+ if dErr = downloader .WriteTo (reader , targetDir , targetFile ); dErr == nil {
173+ binaryPath := filepath .Join (targetDir , targetFile )
174+ s .startPlugin (socket , binaryPath , name )
175+ } else {
176+ serverLogger .Error (dErr , "failed to save extension" , "targetFile" , targetFile )
177+ }
178+ }()
179+ }
180+
148181func (s * storeExtManager ) Start (name , socket string ) (err error ) {
149182 if name == "" {
183+ return nil
150184 }
151185
152186 serverLogger .Info ("start" , "extension" , name , "socket" , socket )
153187 if v , ok := s .extStatusMap [name ]; ok && v {
154- return
188+ return nil
155189 }
156190
157- platformBasedName := name
158- if s .execer .OS () == "windows" {
159- platformBasedName += ".exe"
160- } else {
191+ if s .execer .OS () != "windows" {
161192 socket = fmt .Sprintf ("unix://%s" , home .GetExtensionSocketPath (name ))
162193 }
163194
164- targetDir := home .GetUserBinDir ()
165- targetBinaryFile := filepath .Join (targetDir , platformBasedName )
166-
167- var binaryPath string
168- if _ , err = os .Stat (targetBinaryFile ); err == nil {
169- binaryPath = targetBinaryFile
170- } else {
171- serverLogger .Info ("failed to find extension" , "error" , err .Error ())
172-
173- binaryPath , err = s .execer .LookPath (platformBasedName )
174- if err != nil {
175- err = fmt .Errorf ("not found extension, try to download it, error: %v" , err )
176- go func () {
177- ociDownloader := downloader .NewStoreDownloader ()
178- ociDownloader .WithKind ("store" )
179- ociDownloader .WithOS (s .execer .OS ())
180- reader , dErr := ociDownloader .Download (name , "" , "" )
181- if dErr != nil {
182- serverLogger .Error (dErr , "failed to download extension" , "name" , name )
183- } else {
184- extFile := ociDownloader .GetTargetFile ()
185-
186- targetFile := filepath .Base (extFile )
187- if dErr = downloader .WriteTo (reader , targetDir , targetFile ); dErr == nil {
188- binaryPath = filepath .Join (targetDir , targetFile )
189- s .startPlugin (socket , binaryPath , name )
190- } else {
191- serverLogger .Error (dErr , "failed to save extension" , "targetFile" , targetFile )
192- }
193- }
194- }()
195- }
195+ binaryPath , err := s .resolveBinaryPath (name )
196+ if err != nil {
197+ s .downloadExtensionAsync (name , socket )
198+ return err
196199 }
197200
198- if err == nil {
199- go s .startPlugin (socket , binaryPath , name )
200- }
201- return
201+ go s .startPlugin (socket , binaryPath , name )
202+ return nil
202203}
203204
204205func (s * storeExtManager ) startPlugin (socketURL , plugin , pluginName string ) (err error ) {
@@ -320,25 +321,34 @@ func (s *storeExtManager) startAIHealthMonitoring() {
320321 }()
321322}
322323
323- // performHealthCheck performs health checks on all registered AI plugins
324- func (s * storeExtManager ) performHealthCheck () {
324+ func (s * storeExtManager ) getRegisteredPlugins () map [string ]AIPluginInfo {
325325 s .lock .RLock ()
326+ defer s .lock .RUnlock ()
326327 plugins := make (map [string ]AIPluginInfo )
327328 for name , info := range s .aiPluginRegistry {
328329 plugins [name ] = info
329330 }
330- s .lock .RUnlock ()
331+ return plugins
332+ }
333+
334+ func (s * storeExtManager ) createErrorHealth (name string , err error ) * AIPluginHealth {
335+ return & AIPluginHealth {
336+ Name : name ,
337+ Status : "error" ,
338+ LastCheckAt : time .Now (),
339+ ErrorMessage : err .Error (),
340+ }
341+ }
342+
343+ // performHealthCheck performs health checks on all registered AI plugins
344+ func (s * storeExtManager ) performHealthCheck () {
345+ plugins := s .getRegisteredPlugins ()
331346
332347 for name , info := range plugins {
333348 health , err := s .checkSingleAIPlugin (info )
334349 if err != nil {
335350 serverLogger .Error (err , "Failed to check AI plugin health" , "plugin" , name )
336- health = & AIPluginHealth {
337- Name : name ,
338- Status : "error" ,
339- LastCheckAt : time .Now (),
340- ErrorMessage : err .Error (),
341- }
351+ health = s .createErrorHealth (name , err )
342352 }
343353
344354 s .lock .Lock ()
@@ -419,30 +429,29 @@ func (s *storeExtManager) CheckAIPluginHealth(name string) (*AIPluginHealth, err
419429 return health , nil
420430}
421431
432+ func (s * storeExtManager ) copyHealthInfo (health * AIPluginHealth ) * AIPluginHealth {
433+ copy := & AIPluginHealth {
434+ Name : health .Name ,
435+ Status : health .Status ,
436+ LastCheckAt : health .LastCheckAt ,
437+ ResponseTime : health .ResponseTime ,
438+ ErrorMessage : health .ErrorMessage ,
439+ Metrics : make (map [string ]string ),
440+ }
441+ for k , v := range health .Metrics {
442+ copy .Metrics [k ] = v
443+ }
444+ return copy
445+ }
446+
422447// GetAllAIPluginHealth returns the health status of all AI plugins
423448func (s * storeExtManager ) GetAllAIPluginHealth () (map [string ]* AIPluginHealth , error ) {
424449 s .lock .RLock ()
425450 defer s .lock .RUnlock ()
426451
427- // Return a copy to avoid concurrent access issues
428452 healthMap := make (map [string ]* AIPluginHealth )
429453 for name , health := range s .aiPluginHealthMap {
430- // Create a copy of the health struct
431- healthCopy := & AIPluginHealth {
432- Name : health .Name ,
433- Status : health .Status ,
434- LastCheckAt : health .LastCheckAt ,
435- ResponseTime : health .ResponseTime ,
436- ErrorMessage : health .ErrorMessage ,
437- Metrics : make (map [string ]string ),
438- }
439-
440- // Copy metrics map
441- for k , v := range health .Metrics {
442- healthCopy .Metrics [k ] = v
443- }
444-
445- healthMap [name ] = healthCopy
454+ healthMap [name ] = s .copyHealthInfo (health )
446455 }
447456
448457 return healthMap , nil
0 commit comments