Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Fix for issue #1167, added handling of errors which are returned by scaner #1253

Merged
merged 1 commit into from
Oct 11, 2016

Conversation

katarzyna-z
Copy link
Contributor

Fixes #1167

Hanging of plugins during capturing of stderr and stdout were observed when log line had a significant length. It was caused by errors occured during scanning of stderr or stdout. Errors returned by Scan() were not handled by snapd.

To fix the issue used the method which was used before adding of log capturing from plugin commit so now goto procedure is in use to deal with long line of logs.

Summary of changes:

  • Added handling of errors returned by Scan method in the part of code which is used to capture stdout and stderr from plugins,
  • Modified mock plugin to test log lines with significant length.

Testing done:

  • Verified tests pass for small, medium, and legacy.
  • Manual tests with mock plugin.

@intelsdi-x/snap-maintainers

if err := stdOutScanner.Err(); err != nil {
if err == bufio.ErrTooLong {
reader := bufio.NewReader(e.stdout)
log, _, _ := reader.ReadLine()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why Readline vs ReadString('\n')?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see any special reasons to use Readline() instead of ReadString('\n'). I changed Readline()to ReadString('\n') to avoid parsing to string.

@@ -115,6 +115,7 @@ func (e *ExecutablePlugin) Run(timeout time.Duration) (Response, error) {
e.cmd.Start()
e.captureStderr()
go func() {
OK:
Copy link
Contributor

@IRCody IRCody Oct 4, 2016

Choose a reason for hiding this comment

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

I think this would be clearer as a loop vs a goto.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I changed it, now loops are in use.

if c, ok := mts[i].Config().Table()["long_stderr_log"]; ok && c.(ctypes.ConfigValueBool).Value {
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
longLine := []byte{}
for i := 0; i < bufio.MaxScanTokenSize; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the goal here to append to longline until it is larger than the MaxScanTokenSize?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here the goal is to have longline with length equal to MaxScanTokenSize. The condition in Scan() method has following form:

if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 {
                s.setErr(ErrTooLong)
                return false
}

@katarzyna-z katarzyna-z force-pushed the fix-issue#1167 branch 4 times, most recently from 1bc76c1 to d30b49f Compare October 5, 2016 14:08
WithField("scanner_err", stdOutScanner.Err()).
WithField("read_string_err", err).
Warn(log)
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this continue is needed.

Also there is still a case where we will want to break out of the loop right? Otherwise this goroutine will never exit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made a small correction in code to finish the loop after scanning. Currently, I don't see solution without continue. Please take a look at this. If you have another idea, I'm open for your suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

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

In the iteration I was commenting on the continue served no purpose as there wasn't code executed after it. The update to add break below it changes that behavior.

Copy link
Collaborator

@jcooklin jcooklin left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@jcooklin jcooklin left a comment

Choose a reason for hiding this comment

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

I'm investigating an issue I found with this PR... Disregard my previous LGTM. More detail to come.


if err := stdErrScanner.Err(); err != nil {
reader := bufio.NewReader(e.stderr)
log, err := reader.ReadString('\n')
Copy link
Collaborator

@jcooklin jcooklin Oct 7, 2016

Choose a reason for hiding this comment

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

we need to handle the err from ReadString

log, err2 := reader.ReadString('\n')
                if err2 == io.EOF {
                    break
                }

Without the above block when the plugin stops the go routine never exits. You can see this by loading the modified mock2 plugin and starting the following task and then finally stop (ctrl-c) snapd.

---
  version: 1
  schedule: 
    type: "simple"
    interval: "1s"
  max-failures: 10
  workflow: 
    collect: 
      metrics: 
        /intel/mock/foo: {}
        /intel/mock/bar: {}        
      config: 
        /intel/mock: 
          name: "root"
          password: "secret"          
          long_stderr_log: true

img

WithField("plugin", path.Base(e.cmd.Path())).
WithField("io", "stderr").
WithField("scanner_err", stdErrScanner.Err()).
WithField("read_string_err", err).
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to called stdErrScanner.Err() twice (use err).


if err := stdOutScanner.Err(); err != nil {
reader := bufio.NewReader(e.stdout)
log, err := reader.ReadString('\n')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comments made below on the stderr routine apply here (re: handle ReadString error).

@katarzyna-z katarzyna-z force-pushed the fix-issue#1167 branch 2 times, most recently from 373f43b to 2283c8d Compare October 10, 2016 09:47
@katarzyna-z
Copy link
Contributor Author

Thanks for review. I made a correction. Could you check it?

@IRCody
Copy link
Contributor

IRCody commented Oct 10, 2016

LGTM. @jcooklin?

Copy link
Collaborator

@jcooklin jcooklin left a comment

Choose a reason for hiding this comment

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

LGTM

@IRCody IRCody merged commit b959e8e into intelsdi-x:master Oct 11, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Errors during capturing of stderr from plugins
3 participants