nydus: wait nydusd API server ready before mounting share fs

If the API server is not ready, the mount call will fail, so before
mounting share fs, we should wait the nydusd is started and
the API server is ready.

Fixes: #4710

Signed-off-by: liubin <liubin0329@gmail.com>
Signed-off-by: Bin Liu <bin@hyper.sh>
This commit is contained in:
liubin 2022-07-21 15:42:50 +08:00 committed by Bin Liu
parent 6d56cdb9ac
commit 2ae807fd29
2 changed files with 55 additions and 7 deletions

View File

@ -36,6 +36,8 @@ const (
infoEndpoint = "http://unix/api/v1/daemon" infoEndpoint = "http://unix/api/v1/daemon"
mountEndpoint = "http://unix/api/v1/mount" mountEndpoint = "http://unix/api/v1/mount"
nydusdDaemonStateRunning = "RUNNING"
nydusdStopTimeoutSecs = 5 nydusdStopTimeoutSecs = 5
defaultHttpClientTimeoutSecs = 30 * time.Second defaultHttpClientTimeoutSecs = 30 * time.Second
@ -73,6 +75,7 @@ var (
type nydusd struct { type nydusd struct {
startFn func(cmd *exec.Cmd) error // for mock testing startFn func(cmd *exec.Cmd) error // for mock testing
waitFn func() error // for mock
setupShareDirFn func() error // for mock testing setupShareDirFn func() error // for mock testing
path string path string
sockPath string sockPath string
@ -115,17 +118,19 @@ func (nd *nydusd) Start(ctx context.Context, onQuit onQuitFunc) (int, error) {
"path": nd.path, "path": nd.path,
"args": strings.Join(args, " "), "args": strings.Join(args, " "),
} }
nd.Logger().WithFields(fields).Info() nd.Logger().WithFields(fields).Info("starting nydusd")
if err := nd.startFn(cmd); err != nil { if err := nd.startFn(cmd); err != nil {
return pid, err return pid, errors.Wrap(err, "failed to start nydusd")
} }
nd.Logger().WithFields(fields).Info("nydusd started")
// Monitor nydusd's stdout/stderr and stop sandbox if nydusd quits // Monitor nydusd's stdout/stderr and stop sandbox if nydusd quits
go func() { go func() {
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
nd.Logger().Info(scanner.Text()) nd.Logger().Info(scanner.Text())
} }
nd.Logger().Info("nydusd quits") nd.Logger().Warn("nydusd quits")
// Wait to release resources of nydusd process // Wait to release resources of nydusd process
_, err = cmd.Process.Wait() _, err = cmd.Process.Wait()
if err != nil { if err != nil {
@ -135,9 +140,24 @@ func (nd *nydusd) Start(ctx context.Context, onQuit onQuitFunc) (int, error) {
onQuit() onQuit()
} }
}() }()
if err := nd.setupShareDirFn(); err != nil {
return pid, err nd.Logger().Info("waiting nydusd API server ready")
waitFn := nd.waitUntilNydusAPIServerReady
// waitFn may be set by a mock function for test
if nd.waitFn != nil {
waitFn = nd.waitFn
} }
if err := waitFn(); err != nil {
return pid, errors.Wrap(err, "failed to wait nydusd API server ready")
}
nd.Logger().Info("nydusd API server ready, begin to setup share dir")
if err := nd.setupShareDirFn(); err != nil {
return pid, errors.Wrap(err, "failed to setup share dir for nydus")
}
nd.Logger().Info("nydusd setup share dir completed")
nd.pid = cmd.Process.Pid nd.pid = cmd.Process.Pid
return nd.pid, nil return nd.pid, nil
} }
@ -195,17 +215,39 @@ func (nd *nydusd) valid() error {
} }
func (nd *nydusd) setupPassthroughFS() error { func (nd *nydusd) setupPassthroughFS() error {
nd.Logger().WithField("from", nd.sourcePath).
WithField("dest", sharedPathInGuest).Info("prepare mount passthroughfs")
nc, err := NewNydusClient(nd.apiSockPath) nc, err := NewNydusClient(nd.apiSockPath)
if err != nil { if err != nil {
return err return err
} }
nd.Logger().WithField("from", nd.sourcePath).
WithField("dest", sharedPathInGuest).Info("prepare mount passthroughfs")
mr := NewMountRequest(nydusPassthroughfs, nd.sourcePath, "") mr := NewMountRequest(nydusPassthroughfs, nd.sourcePath, "")
return nc.Mount(sharedPathInGuest, mr) return nc.Mount(sharedPathInGuest, mr)
} }
func (nd *nydusd) waitUntilNydusAPIServerReady() error {
return retry.Do(func() error {
nc, err := NewNydusClient(nd.apiSockPath)
if err != nil {
return err
}
di, err := nc.CheckStatus()
if err != nil {
return err
}
if di.State == nydusdDaemonStateRunning {
return nil
}
return fmt.Errorf("Nydusd daemon is not running: %s", di.State)
},
retry.Attempts(20),
retry.LastErrorOnly(true),
retry.Delay(20*time.Millisecond))
}
func (nd *nydusd) Mount(opt MountOption) error { func (nd *nydusd) Mount(opt MountOption) error {
nc, err := NewNydusClient(nd.apiSockPath) nc, err := NewNydusClient(nd.apiSockPath)
if err != nil { if err != nil {

View File

@ -28,6 +28,7 @@ func TestNydusdStart(t *testing.T) {
debug bool debug bool
extraArgs []string extraArgs []string
startFn func(cmd *exec.Cmd) error startFn func(cmd *exec.Cmd) error
waitFn func() error
setupShareDirFn func() error setupShareDirFn func() error
} }
@ -46,6 +47,9 @@ func TestNydusdStart(t *testing.T) {
cmd.Process = &os.Process{} cmd.Process = &os.Process{}
return nil return nil
}, },
waitFn: func() error {
return nil
},
setupShareDirFn: func() error { return nil }, setupShareDirFn: func() error { return nil },
} }
SourcePathNoExist := validConfig SourcePathNoExist := validConfig
@ -72,9 +76,11 @@ func TestNydusdStart(t *testing.T) {
debug: tt.fields.debug, debug: tt.fields.debug,
pid: tt.fields.pid, pid: tt.fields.pid,
startFn: tt.fields.startFn, startFn: tt.fields.startFn,
waitFn: tt.fields.waitFn,
setupShareDirFn: tt.fields.setupShareDirFn, setupShareDirFn: tt.fields.setupShareDirFn,
} }
ctx := context.Background() ctx := context.Background()
_, err := nd.Start(ctx, nil) _, err := nd.Start(ctx, nil)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("nydusd.Start() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("nydusd.Start() error = %v, wantErr %v", err, tt.wantErr)