@@ -11,6 +11,7 @@ import (
1111 "os/exec"
1212 "strings"
1313 "sync"
14+ "sync/atomic"
1415 "time"
1516
1617 "github.com/awnumar/memguard"
@@ -217,16 +218,7 @@ func (a *API) SetData(ctx context.Context, logger zerolog.Logger, data PostInitJ
217218 }
218219
219220 if data .VolumeMounts != nil {
220- var wg sync.WaitGroup
221- for _ , volume := range * data .VolumeMounts {
222- logger .Debug ().Msgf ("Mounting %s at %q" , volume .NfsTarget , volume .Path )
223-
224- wg .Go (func () {
225- a .setupNfs (context .WithoutCancel (ctx ), volume .NfsTarget , volume .Path )
226- })
227- }
228-
229- wg .Wait ()
221+ a .setupNFS (ctx , logger , * data .VolumeMounts )
230222 }
231223
232224 return nil
@@ -247,7 +239,61 @@ var nfsOptions = strings.Join([]string{
247239 "noacl" , // no reason for acl in the sandbox
248240}, "," )
249241
250- func (a * API ) setupNfs (ctx context.Context , nfsTarget , path string ) {
242+ const nfsMountTimeout = 5 * time .Second
243+
244+ func (a * API ) setupNFS (ctx context.Context , logger zerolog.Logger , mounts []VolumeMount ) {
245+ // Already fully mounted, nothing to do
246+ if a .isMountedNFS .Load () {
247+ logger .Debug ().Msg ("NFS volumes already mounted" )
248+
249+ return
250+ }
251+
252+ // Prevent concurrent mounting attempts
253+ if ! a .isMountingNFS .CompareAndSwap (false , true ) {
254+ logger .Debug ().Msg ("NFS volumes already mounting" )
255+
256+ return
257+ }
258+ defer a .isMountingNFS .Store (false )
259+
260+ logger .Debug ().Msg ("Mounting NFS volumes" )
261+
262+ ctx = context .WithoutCancel (ctx )
263+ ctx , cancel := context .WithTimeout (ctx , nfsMountTimeout )
264+ defer cancel ()
265+
266+ var wg sync.WaitGroup
267+ var allSucceeded atomic.Bool
268+ allSucceeded .Store (true )
269+
270+ for _ , volume := range mounts {
271+ // Skip already mounted paths
272+ if _ , ok := a .mountedPaths .Load (volume .Path ); ok {
273+ logger .Debug ().Msgf ("Skipping already mounted %q" , volume .Path )
274+
275+ continue
276+ }
277+
278+ logger .Debug ().Msgf ("Mounting %s at %q" , volume .NfsTarget , volume .Path )
279+
280+ wg .Go (func () {
281+ if a .mountNFS (ctx , volume .NfsTarget , volume .Path ) {
282+ a .mountedPaths .Store (volume .Path , true )
283+ } else {
284+ allSucceeded .Store (false )
285+ }
286+ })
287+ }
288+
289+ wg .Wait ()
290+
291+ if allSucceeded .Load () {
292+ a .isMountedNFS .Store (true )
293+ }
294+ }
295+
296+ func (a * API ) mountNFS (ctx context.Context , nfsTarget , path string ) bool {
251297 commands := [][]string {
252298 {"mkdir" , "-p" , path },
253299 {"mount" , "-v" , "-t" , "nfs" , "-o" , "fg,hard," + nfsOptions , nfsTarget , path },
@@ -264,9 +310,11 @@ func (a *API) setupNfs(ctx context.Context, nfsTarget, path string) {
264310 Msg ("Mount NFS" )
265311
266312 if err != nil {
267- return
313+ return false
268314 }
269315 }
316+
317+ return true
270318}
271319
272320func (a * API ) SetupHyperloop (address string ) {
0 commit comments