@@ -57,6 +57,8 @@ type Box struct {
5757 muContainers sync.Mutex
5858 containers map [string ]* container
5959 blobRepo BlobRepository
60+
61+ onClose context.CancelFunc
6062}
6163
6264// NewBox creates new Box
@@ -111,19 +113,95 @@ func NewBox(ctx context.Context, cfg isolate.BoxConfig) (isolate.Box, error) {
111113 },
112114 }
113115
116+ ctx , onClose := context .WithCancel (ctx )
114117 box := & Box {
115118 config : config ,
116119 instanceID : uuid .New (),
117120 transport : tr ,
118121 spawnSM : semaphore .New (config .SpawnConcurrency ),
119122 containers : make (map [string ]* container ),
123+ onClose : onClose ,
120124
121125 blobRepo : blobRepo ,
122126 }
123127
128+ go box .waitLoop (ctx )
129+
124130 return box , nil
125131}
126132
133+ func (b * Box ) waitLoop (ctx context.Context ) {
134+ apexctx .GetLogger (ctx ).Info ("start waitLoop" )
135+ var (
136+ portoConn porto.API
137+ err error
138+ )
139+
140+ var waitTimeout = 30 * time .Second
141+
142+ closed := func (portoConn porto.API ) bool {
143+ select {
144+ case <- ctx .Done ():
145+ if portoConn != nil {
146+ portoConn .Close ()
147+ }
148+ return true
149+ default :
150+ return false
151+ }
152+ }
153+
154+ LOOP:
155+ for {
156+ apexctx .GetLogger (ctx ).Info ("next iteration of waitLoop" )
157+ if closed (portoConn ) {
158+ return
159+ }
160+ // Connect to Porto if we have not connected yet.
161+ // In case of error: wait either a fixed timeout or closing of Box
162+ if portoConn == nil {
163+ apexctx .GetLogger (ctx ).Info ("waitLoop: connect to Portod" )
164+ portoConn , err = porto .Connect ()
165+ if err != nil {
166+ apexctx .GetLogger (ctx ).WithError (err ).Warn ("unable to connect to Portod" )
167+ select {
168+ case <- time .After (time .Second ):
169+ continue LOOP
170+ case <- ctx .Done ():
171+ return
172+ }
173+ }
174+ }
175+
176+ // * means all containers
177+ // if no containers dead for waitTimeout, name will be an empty string
178+ containerName , err := portoConn .Wait ([]string {"*" }, 30 * waitTimeout )
179+ if err != nil {
180+ portoConn .Close ()
181+ portoConn = nil
182+ continue LOOP
183+ }
184+
185+ if containerName != "" {
186+ apexctx .GetLogger (ctx ).Infof ("Wait reports %s to be dead" , containerName )
187+ b .muContainers .Lock ()
188+ container , ok := b .containers [containerName ]
189+ if ok {
190+ delete (b .containers , containerName )
191+ }
192+ rest := len (b .containers )
193+ b .muContainers .Unlock ()
194+ if ok {
195+ if err = container .Kill (); err != nil {
196+ apexctx .GetLogger (ctx ).WithError (err ).Errorf ("Killing %s error" , containerName )
197+ }
198+ }
199+
200+ apexctx .GetLogger (ctx ).Infof ("%d containers are being tracked now" , rest )
201+ }
202+ }
203+ }
204+
127205func (b * Box ) appLayerName (appname string ) string {
128206 return b .instanceID + appname
129207}
@@ -271,5 +349,6 @@ func (b *Box) Spawn(ctx context.Context, config isolate.SpawnConfig, output io.W
271349// Close releases all resources such as idle connections from http.Transport
272350func (b * Box ) Close () error {
273351 b .transport .CloseIdleConnections ()
352+ b .onClose ()
274353 return nil
275354}
0 commit comments