Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add lifecycle validation #558

Merged
merged 1 commit into from
Feb 5, 2018

Conversation

zhouhao3
Copy link

@zhouhao3 zhouhao3 commented Jan 22, 2018

I am going to add a validation of the lifecycle, the specific norms are as follows:

The pre-start hooks MUST be called after the start operation is called but before the user-specified program command is executed.

But I can not confirm the specific call state, so for the moment I'm just verifying that the relevant command has not been called.

This is just a preliminary idea. Lifecycle validation also need more work, any comments welcome. Or you can write a new patch.

Signed-off-by: Zhou Hao zhouhao@cn.fujitsu.com

@liangchenye
Copy link
Member

The pre-start hooks MUST be called after the start operation is called but before the user-specified program command is executed.

Before 'start', we have 'create' operation, so I think this spec means:

  1. when 'create', pre-start hooks SHOULD not be called
  2. only when 'start', pre-start hooks will be called and be called after process

How about make a testcase like this:

  1. in 'pre-start hook' we can set a command echo "pre-start called" >> $bundle/$rootfs/output
  2. in 'process' we can set a command echo "process called" >> $bundle/$rootfs/output

so when 'create', the $bundle/$rootfs/output should be empty,
when 'start, the $bundle/$rootfs/output should look like this:
pre-start called process called

@liangchenye
Copy link
Member

I think RuntimeOutsideValidate is not suitable for lifecycle test.
We need to add 'preStart', 'postStart', 'postStop' functions as the arguments.
I try to do it in #557.

@zhouhao3
Copy link
Author

I've already seen #557, and the setting of LifecycleFuncs is good. I will wait for #557 to merge and then update this patch.

@zhouhao3 zhouhao3 added this to the v0.5.0 milestone Jan 24, 2018
@zhouhao3
Copy link
Author

According to the description of lifecycle, I have determined the order of calls for each part:

create -> start -> prestart -> process -> poststart -> delete -> poststop

The code is updated according to this call order.
@liangchenye @wking @Mashimiao PTAL

return err
}
if string(outputData) != "" {
return errors.New("Wrong call")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure what to say about this type of error. Any suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure what to say about this type of error.

The associated specerror entries start around here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on what is read, the specific error is different. It would be very troublesome to be accurate to every error condition. So instead of making a further distinction, I want to output the error directly.

Do you think it is necessary to distinguish between each error?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, each error should explicitly reflect the relevant 'specerror'.

t.Header(0)

prestart := rspec.Hook{
Path: "/usr/bin/prestart",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does /usr/bin/prestart come from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About this path, I thought it was created automatically when added.
Maybe this idea is wrong.
Do you have any suggestions for the path?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any suggestions for the path?

The echo on our BusyBox tarball?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm writing a demo to show my idea, I'll link to you soom.

@liangchenye
Copy link
Member

Following your idea, I made some changes: https://github.com/liangchenye/ocitools/blob/gotest/validation/lifecycle.go

  1. using 'sh' and 'echo' in the bundle
  2. compose hooks in 'preCreate'

About the 'Wrong Error", I suggest we may split the lifecycle.go to 'prestart.go', 'poststart.go' and the etc. Each hook has multiply spec errors, it will make it easier to maintain and cover all the errors. How do you think ? @q384566678

@wking
Copy link
Contributor

wking commented Feb 1, 2018 via email

@zhouhao3
Copy link
Author

zhouhao3 commented Feb 1, 2018

@liangchenye That sounds good. I'll update patch in this way. Thank you for your advice.

@zhouhao3 zhouhao3 force-pushed the lifecycle-validation branch 2 times, most recently from 1c6387c to 4ad25ac Compare February 1, 2018 07:34
@zhouhao3
Copy link
Author

zhouhao3 commented Feb 1, 2018

updated, @liangchenye @wking PTAL.

},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is expected to get an 'err', since the 'output' will not exist.

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should not happen, we can move it to 'default' part.

switch string(outputData) {
case "":
return nil
case "post-start called\n":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make it simpler:

if strings.Contains(string(outputData), "post-start") {
     return specerror.NewError(specerror.PoststartTimin
} else if strings.Contains(string(outputData), "process") {
     return  specerror.NewError(specerror.ProcNotRunAtResRequest)
}

PreDelete: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also

return fmt.Errorf("%v\n%v", specerror.NewError(specerror.PoststartHooksInvoke, 

@liangchenye
Copy link
Member

liangchenye commented Feb 1, 2018

I commented in prestart.go and it misses one case:

 If any prestart hook fails, the runtime MUST generate an error, stop the container, and continue the lifecycle at step 9.

},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to 'poststart.go', we should get an error: the output file is not exist.

},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, should get an error.

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"" could merge with default

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a part of 'default'

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a part of 'default'

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of 'default'

return specerror.NewError(specerror.PrestartTiming, fmt.Errorf("Pre-start hooks MUST be called after the `start` operation is called"), rspec.Version)
case "process called\n":
return specerror.NewError(specerror.ProcNotRunAtResRequest, fmt.Errorf("The user-specified program (from process) MUST NOT be run at this time"), rspec.Version)
case "pre-start called\nprocess called\n", "process called\npre-start called\n":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this, change

case "pre-start called\n" 

to

strings.Contain

return err
}
switch string(outputData) {
case "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a part of default

@liangchenye
Copy link
Member

Same to other hooks.

 If any poststart hook fails, the runtime MUST log a warning, but the remaining hooks and lifecycle continue as if the hook had succeeded.
if any poststop hook fails, the runtime MUST log a warning, but the remaining hooks and lifecycle continue as if the hook had succeeded.

@zhouhao3 zhouhao3 force-pushed the lifecycle-validation branch 2 times, most recently from 92b6907 to 70a423b Compare February 2, 2018 03:04
@zhouhao3 zhouhao3 force-pushed the lifecycle-validation branch 2 times, most recently from 3c3b7d6 to 8dca66d Compare February 2, 2018 06:15
@zhouhao3
Copy link
Author

zhouhao3 commented Feb 2, 2018

@liangchenye @wking updated, PTAL.

@zhouhao3 zhouhao3 force-pushed the lifecycle-validation branch 2 times, most recently from 1c2ac39 to 32fe314 Compare February 2, 2018 06:54
@liangchenye
Copy link
Member

liangchenye commented Feb 2, 2018

It looks to me. (but I'm waiting for another day to merge it.)
In my test, I found runc has three issues:

  1. prestart/poststart hook is called in 'create'
    2. when the poststop hook was set, the main process never work

I'll doubt check the runc code.

@liangchenye
Copy link
Member

liangchenye commented Feb 2, 2018

I saw another problem in the test code, the process 'output' filepath is wrong, it is already running inside the container.

g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", output)})

}

g.AddPostStartHook(poststart)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", output)})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to:
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", "/output")})

}
return nil
},
PreDelete: func(r *util.Runtime) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we can add

util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second * 10, time.Second)

'start' will return immediately, although 'echo' runs fast, but we are not sure how many time it costs. (for example, in runc/cc/Kata project)

PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err == nil {
if strings.Contains(string(outputData), "post-start called") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always got it in 'runc'!

}

g.AddPreStartHook(prestart)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", output)})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, the output is wrong.

return nil
},
PreDelete: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, I suggest to add 'Wait.."

PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err == nil {
if strings.Contains(string(outputData), "pre-start called") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always get this in 'runc'.

case "process called\n":
fmt.Fprintln(os.Stderr, "WARNING: The poststart hook invoke fails")
return nil
case "post-start called\nprocess called\n":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since 'post-start' runs in 'create', the sequence here is wrong.

}

g.AddPostStopHook(poststop)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", output)})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, wrong output

@liangchenye
Copy link
Member

Report an issue in 'runc' project: opencontainers/runc#1710.

Signed-off-by: Zhou Hao <zhouhao@cn.fujitsu.com>
@liangchenye
Copy link
Member

liangchenye commented Feb 5, 2018

LGTM

Approved with PullApprove

@liangchenye liangchenye merged commit 59053a6 into opencontainers:master Feb 5, 2018
@zhouhao3 zhouhao3 deleted the lifecycle-validation branch February 5, 2018 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants