11/*
2- Copyright 2023-2024 API Testing Authors.
2+ Copyright 2023-2025 API Testing Authors.
33
44Licensed under the Apache License, Version 2.0 (the "License");
55you may not use this file except in compliance with the License.
@@ -16,160 +16,172 @@ limitations under the License.
1616package server
1717
1818import (
19- "context"
20- "errors"
21- "fmt"
22- "io"
23- "net/http"
24- "os"
25- "path/filepath"
26- "strings"
27- sync "sync"
28- "syscall"
29- "time"
30-
31- "github.com/linuxsuren/api-testing/pkg/util/home"
32-
33- "github.com/linuxsuren/api-testing/pkg/downloader"
34- "github.com/linuxsuren/api-testing/pkg/logging"
35-
36- fakeruntime "github.com/linuxsuren/go-fake-runtime"
19+ "context"
20+ "errors"
21+ "fmt"
22+ "io"
23+ "net/http"
24+ "os"
25+ "path/filepath"
26+ "strings"
27+ sync "sync"
28+ "syscall"
29+ "time"
30+
31+ "github.com/linuxsuren/api-testing/pkg/util/home"
32+
33+ "github.com/linuxsuren/api-testing/pkg/downloader"
34+ "github.com/linuxsuren/api-testing/pkg/logging"
35+
36+ fakeruntime "github.com/linuxsuren/go-fake-runtime"
3737)
3838
3939var (
40- serverLogger = logging .DefaultLogger (logging .LogLevelInfo ).WithName ("server" )
40+ serverLogger = logging .DefaultLogger (logging .LogLevelInfo ).WithName ("server" )
4141)
4242
4343type ExtManager interface {
44- Start (name , socket string ) (err error )
45- StopAll () (err error )
46- WithDownloader (downloader.PlatformAwareOCIDownloader )
44+ Start (name , socket string ) (err error )
45+ StopAll () (err error )
46+ WithDownloader (downloader.PlatformAwareOCIDownloader )
4747}
4848
4949type storeExtManager struct {
50- execer fakeruntime.Execer
51- ociDownloader downloader.PlatformAwareOCIDownloader
52- socketPrefix string
53- filesNeedToBeRemoved []string
54- extStatusMap map [string ]bool
55- processs []fakeruntime.Process
56- processChan chan fakeruntime.Process
57- stopSingal chan struct {}
58- lock * sync.RWMutex
50+ execer fakeruntime.Execer
51+ ociDownloader downloader.PlatformAwareOCIDownloader
52+ socketPrefix string
53+ filesNeedToBeRemoved []string
54+ extStatusMap map [string ]bool
55+ processs []fakeruntime.Process
56+ processChan chan fakeruntime.Process
57+ stopSingal chan struct {}
58+ lock * sync.RWMutex
5959}
6060
6161var ss * storeExtManager
6262
6363func NewStoreExtManager (execer fakeruntime.Execer ) ExtManager {
64- if ss == nil {
65- ss = & storeExtManager {
66- processChan : make (chan fakeruntime.Process ),
67- stopSingal : make (chan struct {}, 1 ),
68- lock : & sync.RWMutex {},
69- }
70- ss .execer = execer
71- ss .socketPrefix = "unix://"
72- ss .extStatusMap = map [string ]bool {}
73- ss .processCollect ()
74- ss .WithDownloader (& nonDownloader {})
75- }
76- return ss
64+ if ss == nil {
65+ ss = & storeExtManager {
66+ processChan : make (chan fakeruntime.Process ),
67+ stopSingal : make (chan struct {}, 1 ),
68+ lock : & sync.RWMutex {},
69+ }
70+ ss .execer = execer
71+ ss .socketPrefix = "unix://"
72+ ss .extStatusMap = map [string ]bool {}
73+ ss .processCollect ()
74+ ss .WithDownloader (& nonDownloader {})
75+ }
76+ return ss
7777}
7878
7979func NewStoreExtManagerInstance (execer fakeruntime.Execer ) ExtManager {
80- ss = & storeExtManager {
81- processChan : make (chan fakeruntime.Process ),
82- stopSingal : make (chan struct {}, 1 ),
83- lock : & sync.RWMutex {},
84- }
85- ss .execer = execer
86- ss .socketPrefix = "unix://"
87- ss .extStatusMap = map [string ]bool {}
88- ss .processCollect ()
89- ss .WithDownloader (& nonDownloader {})
90- return ss
80+ ss = & storeExtManager {
81+ processChan : make (chan fakeruntime.Process ),
82+ stopSingal : make (chan struct {}, 1 ),
83+ lock : & sync.RWMutex {},
84+ }
85+ ss .execer = execer
86+ ss .socketPrefix = "unix://"
87+ ss .extStatusMap = map [string ]bool {}
88+ ss .processCollect ()
89+ ss .WithDownloader (& nonDownloader {})
90+ return ss
9191}
9292
9393func (s * storeExtManager ) Start (name , socket string ) (err error ) {
94- if v , ok := s .extStatusMap [name ]; ok && v {
95- return
96- }
97- targetDir := home .GetUserBinDir ()
98- targetBinaryFile := filepath .Join (targetDir , name )
99-
100- var binaryPath string
101- if _ , err = os .Stat (targetBinaryFile ); err == nil {
102- binaryPath = targetBinaryFile
103- } else {
104- binaryPath , err = s .execer .LookPath (name )
105- if err != nil {
106- err = fmt .Errorf ("not found extension, try to download it, error: %v" , err )
107- go func () {
108- reader , dErr := s .ociDownloader .Download (name , "" , "" )
109- if dErr != nil {
110- serverLogger .Error (dErr , "failed to download extension" , "name" , name )
111- } else {
112- extFile := s .ociDownloader .GetTargetFile ()
113-
114- targetFile := filepath .Base (extFile )
115- if dErr = downloader .WriteTo (reader , targetDir , targetFile ); dErr == nil {
116- binaryPath = filepath .Join (targetDir , targetFile )
117- s .startPlugin (socket , binaryPath , name )
118- } else {
119- serverLogger .Error (dErr , "failed to save extension" , "targetFile" , targetFile )
120- }
121- }
122- }()
123- }
124- }
125-
126- if err == nil {
127- go s .startPlugin (socket , binaryPath , name )
128- }
129- return
94+ if v , ok := s .extStatusMap [name ]; ok && v {
95+ return
96+ }
97+ targetDir := home .GetUserBinDir ()
98+ targetBinaryFile := filepath .Join (targetDir , name )
99+
100+ var binaryPath string
101+ if _ , err = os .Stat (targetBinaryFile ); err == nil {
102+ binaryPath = targetBinaryFile
103+ } else {
104+ binaryPath , err = s .execer .LookPath (name )
105+ if err != nil {
106+ err = fmt .Errorf ("not found extension, try to download it, error: %v" , err )
107+ go func () {
108+ reader , dErr := s .ociDownloader .Download (name , "" , "" )
109+ if dErr != nil {
110+ serverLogger .Error (dErr , "failed to download extension" , "name" , name )
111+ } else {
112+ extFile := s .ociDownloader .GetTargetFile ()
113+
114+ targetFile := filepath .Base (extFile )
115+ if dErr = downloader .WriteTo (reader , targetDir , targetFile ); dErr == nil {
116+ binaryPath = filepath .Join (targetDir , targetFile )
117+ s .startPlugin (socket , binaryPath , name )
118+ } else {
119+ serverLogger .Error (dErr , "failed to save extension" , "targetFile" , targetFile )
120+ }
121+ }
122+ }()
123+ }
124+ }
125+
126+ if err == nil {
127+ go s .startPlugin (socket , binaryPath , name )
128+ }
129+ return
130130}
131131
132132func (s * storeExtManager ) startPlugin (socketURL , plugin , pluginName string ) (err error ) {
133- socketFile := strings .TrimPrefix (socketURL , s .socketPrefix )
134- _ = os .RemoveAll (socketFile ) // always deleting the socket file to avoid start failing
133+ if strings .Contains (socketURL , ":" ) && ! strings .HasPrefix (socketURL , s .socketPrefix ) {
134+ err = s .startPluginViaHTTP (socketURL , plugin , pluginName )
135+ return
136+ }
137+ socketFile := strings .TrimPrefix (socketURL , s .socketPrefix )
138+ _ = os .RemoveAll (socketFile ) // always deleting the socket file to avoid start failing
135139
136- s .lock .Lock ()
137- s .filesNeedToBeRemoved = append (s .filesNeedToBeRemoved , socketFile )
138- s .extStatusMap [pluginName ] = true
139- s .lock .Unlock ()
140+ s .lock .Lock ()
141+ s .filesNeedToBeRemoved = append (s .filesNeedToBeRemoved , socketFile )
142+ s .extStatusMap [pluginName ] = true
143+ s .lock .Unlock ()
140144
141- if err = s .execer .RunCommandWithIO (plugin , "" , os .Stdout , os .Stderr , s .processChan , "--socket" , socketFile ); err != nil {
142- serverLogger .Info ("failed to start ext manager" , "socket" , socketURL , "error: " , err .Error ())
143- }
144- return
145+ if err = s .execer .RunCommandWithIO (plugin , "" , os .Stdout , os .Stderr , s .processChan , "--socket" , socketFile ); err != nil {
146+ serverLogger .Info ("failed to start ext manager" , "socket" , socketURL , "error: " , err .Error ())
147+ }
148+ return
149+ }
150+
151+ func (s * storeExtManager ) startPluginViaHTTP (httpURL , plugin , pluginName string ) (err error ) {
152+ port := strings .Split (httpURL , ":" )[1 ]
153+ if err = s .execer .RunCommandWithIO (plugin , "" , os .Stdout , os .Stderr , s .processChan , "--port" , port ); err != nil {
154+ serverLogger .Info ("failed to start ext manager" , "port" , port , "error: " , err .Error ())
155+ }
156+ return
145157}
146158
147159func (s * storeExtManager ) StopAll () error {
148- serverLogger .Info ("stop" , "extensions" , len (s .processs ))
149- for _ , p := range s .processs {
150- if p != nil {
151- p .Signal (syscall .SIGTERM )
152- }
153- }
154- s .stopSingal <- struct {}{}
155- return nil
160+ serverLogger .Info ("stop" , "extensions" , len (s .processs ))
161+ for _ , p := range s .processs {
162+ if p != nil {
163+ p .Signal (syscall .SIGTERM )
164+ }
165+ }
166+ s .stopSingal <- struct {}{}
167+ return nil
156168}
157169
158170func (s * storeExtManager ) WithDownloader (ociDownloader downloader.PlatformAwareOCIDownloader ) {
159- s .ociDownloader = ociDownloader
171+ s .ociDownloader = ociDownloader
160172}
161173
162174func (s * storeExtManager ) processCollect () {
163- go func () {
164- for {
165- select {
166- case p := <- s .processChan :
167- s .processs = append (s .processs , p )
168- case <- s .stopSingal :
169- return
170- }
171- }
172- }()
175+ go func () {
176+ for {
177+ select {
178+ case p := <- s .processChan :
179+ s .processs = append (s .processs , p )
180+ case <- s .stopSingal :
181+ return
182+ }
183+ }
184+ }()
173185}
174186
175187var ErrDownloadNotSupport = errors .New ("no support" )
@@ -179,44 +191,44 @@ type nonDownloader struct{}
179191var _ downloader.PlatformAwareOCIDownloader = & nonDownloader {}
180192
181193func (n * nonDownloader ) WithBasicAuth (username string , password string ) {
182- // Do nothing because this is an empty implementation
194+ // Do nothing because this is an empty implementation
183195}
184196
185197func (n * nonDownloader ) Download (image , tag , file string ) (reader io.Reader , err error ) {
186- err = ErrDownloadNotSupport
187- return
198+ err = ErrDownloadNotSupport
199+ return
188200}
189201
190202func (n * nonDownloader ) WithOS (string ) {
191- // Do nothing because this is an empty implementation
203+ // Do nothing because this is an empty implementation
192204}
193205
194206func (n * nonDownloader ) WithArch (string ) {
195- // Do nothing because this is an empty implementation
207+ // Do nothing because this is an empty implementation
196208}
197209
198210func (n * nonDownloader ) WithRegistry (string ) {
199- // Do nothing because this is an empty implementation
211+ // Do nothing because this is an empty implementation
200212}
201213
202214func (n * nonDownloader ) WithKind (string ) {
203- // Do nothing because this is an empty implementation
215+ // Do nothing because this is an empty implementation
204216}
205217
206218func (n * nonDownloader ) WithImagePrefix (imagePrefix string ) {
207- // Do nothing because this is an empty implementation
219+ // Do nothing because this is an empty implementation
208220}
209221func (d * nonDownloader ) WithRoundTripper (rt http.RoundTripper ) {
210- // Do nothing because this is an empty implementation
222+ // Do nothing because this is an empty implementation
211223}
212224
213225func (d * nonDownloader ) WithInsecure (bool ) {
214- // Do nothing because this is an empty implementation
226+ // Do nothing because this is an empty implementation
215227}
216228
217229func (d * nonDownloader ) WithTimeout (time.Duration ) {}
218230func (d * nonDownloader ) WithContext (context.Context ) {}
219231
220232func (n * nonDownloader ) GetTargetFile () string {
221- return ""
233+ return ""
222234}
0 commit comments