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 Support for Service Task Logs #32015

Merged
merged 1 commit into from Apr 4, 2017

Conversation

dperny
Copy link
Contributor

@dperny dperny commented Mar 22, 2017

This PR probably needs a docs update too. I'm gonna wait until it passes design review before I update the docs

- What I did
Refactored the API to more easily accept new endpoints. Added REST, client, and CLI endpoints for getting logs from a specific task. All that is needed after this commit to enable arbitrary service log selectors is a REST endpoint.

Added a --task flag to docker service logs to get logs for a specific task.

Added search by task if a service wasn't found.

- How I did it
Refactored a bunch of code, added a couple of new endpoints.

- How to verify it
Added a new integration test. You can also try running docker service logs --task [taskid].

- Description for the changelog
docker service logs command now also gets task logs. Added /task/{id}/logs REST endpoint.

- A picture of a cute animal (not mandatory but encouraged)
image

/cc @aluzzardi

@AkihiroSuda
Copy link
Member

related: #29307

cc @stevvooe @aluzzardi @dnephin

Copy link
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

Will task ids and service ids ever overlap?

Could we just accept all the ids without adding a new flag?

r.Form.Add("tasks", vars["id"])
// TODO(dperny) i might should delete the id from vars before handling
// this request? but i don't think it matters
return sr.getSwarmLogs(ctx, w, r, vars)
Copy link
Member

Choose a reason for hiding this comment

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

Could this be passed to getSwarmLogs() with a LogSelector{} param instead of using forms?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if we leave getSwarmLogs as is, with no LogSelector parameter, then later on a route for arbitrary combinations of service and task logs (for example, maybe stack logs?) can be easily constructed. in fact, on local, i had such a route, that looked somewhat like /swarm/logs?service=foo&service=bar&task=wombo and returned a log stream for whatever selectors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(what i mean is getSwarmLogs() can be used verbatim as a handler)

Copy link
Member

Choose a reason for hiding this comment

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

I think it should still be pretty easy to support such a handler, without having to modify the request parameters in the current implementation.

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, then my second question (for you and anyone else) is where is the best place to put the getSwarmLogs function, if it does not conform to the Handler function type? Can I leave it in the cluster_routes.go file? I believe explicitly should NOT go in the daemon, I think the pattern of having the daemon construct the logs byte stream response is Bad and i'm addressing that in a separate PR.

r, w := io.Pipe()
cmd.Stdout = w
cmd.Stderr = w
c.Assert(cmd.Start(), checker.IsNil)
Copy link
Member

Choose a reason for hiding this comment

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

Please use github.com/docker/docker/pkg/testutil/cmd to run commands in tests. It helps ensure that we have consistent (and sufficiently detailed) error messages when things fail, and it should be a lot less verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do, chief.

@dperny
Copy link
Contributor Author

dperny commented Mar 23, 2017

I'm not sure the right options for the CLI UI. What I've presented is my best effort as to what I think is correct. Ideally I'd like to support any arbitrary combination of task/service/node logs to reflect what the swarmkit GRPC can do with log selectors. However, that concept DOES NOT map to the existing REST API paradigm, and, by extension, to the CLI.

Someone is going to come in here, like the last PR about this, and suggest that we do like filters or whatever and I agree with you but I'm really boxed in by the REST paradigm, which is not conducive to arbitrary selectors like GRPC API is. We'd need an extra API endpoint that does have the format /service/{id}/logs, like maybe /swarm/logs?service=wombo&task=combo which DOES map to the LogSelector. That's part of what I've enabled by making that getSwarmLogs() handler like it is; we can just add a new route when we decide what we want that route should be named.

I'm going to alter the CLI to have the pattern docker service logs SERVICE|TASK, no flags. If you want something better, fight me with your PRs. Both fox no items final destination.

@dperny dperny force-pushed the service-logs-support-task-logs branch from 7264087 to c2b5057 Compare March 23, 2017 23:08
@dperny
Copy link
Contributor Author

dperny commented Mar 23, 2017

Fixed the CLI UI. Fixed the tests.

@cowlicks
Copy link

LGTM

@dperny
Copy link
Contributor Author

dperny commented Mar 30, 2017

@dnephin Moved service log handling to a selector as suggested.

c.mu.RUnlock()
return err
// if ANY tasks use a TTY, don't mux streams
var TTY bool
Copy link
Member

Choose a reason for hiding this comment

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

nit: This should be tty

if err != nil {
return err
}
// TODO(dperny) not sure we need this check?
Copy link
Member

Choose a reason for hiding this comment

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

Yes, we do! Good catch

if err != nil {
return err
}
// TODO(dperny) same as above
Copy link
Member

Choose a reason for hiding this comment

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

Same as above as well - we need this check

@aluzzardi
Copy link
Member

Minor comments, overall LGTM

@dperny dperny force-pushed the service-logs-support-task-logs branch from 0635a57 to d82ae95 Compare March 30, 2017 23:48
@dperny
Copy link
Contributor Author

dperny commented Mar 30, 2017

@aluzzardi fixed nits

Copy link
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

SGTM (design wise)
/CC @cpuguy83 @thaJeztah @mlaventure

@@ -70,28 +75,45 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
Tail: opts.tail,
}

client := dockerCli.Client()
// this variable was original called "client" which conflicts with the
Copy link
Member

Choose a reason for hiding this comment

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

This comment can go 👼 (and I agree with the change 😛)

if !client.IsErrServiceNotFound(err) {
return err
}
_, _, err := cli.TaskInspectWithRaw(ctx, opts.target)
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this err un-useful at all ? (and thus the call as well)
@dperny missing an error check here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I totally dropped an error check, good catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This doesn't look addressed but it has been below. I moved the logs call to after the error check.

@dperny dperny force-pushed the service-logs-support-task-logs branch from d82ae95 to ea442cf Compare March 31, 2017 20:19
@dperny
Copy link
Contributor Author

dperny commented Mar 31, 2017

@vdemeester fixed.

@vieux
Copy link
Contributor

vieux commented Mar 31, 2017

@dperny can you update the documentation ? (at least the API doc to add the endpoint)

@dperny
Copy link
Contributor Author

dperny commented Mar 31, 2017

I would like to do so but I am unclear on how to go about it.

@vieux
Copy link
Contributor

vieux commented Mar 31, 2017

you need to edit api/swagger.yaml you can look at service logs for inspiration :P

@dperny dperny force-pushed the service-logs-support-task-logs branch from ea442cf to 0765e98 Compare March 31, 2017 23:31
@dperny
Copy link
Contributor Author

dperny commented Mar 31, 2017

updated api/swagger.yml to reflect task logs. The updated API documentation may not be totally correct (it is copied from service logs, which is also not totally correct for pre-existing reasons) and will need to be updated again before service logs are moved out of experimental.

api/swagger.yaml Outdated
get:
summary: "Get task logs"
description: |
Get `stdout` and `stderr` logs from a service.
Copy link
Contributor

Choose a reason for hiding this comment

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

service > task

api/swagger.yaml Outdated
description: |
Get `stdout` and `stderr` logs from a service.

**Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers.
Copy link
Contributor

Choose a reason for hiding this comment

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

service > task

api/swagger.yaml Outdated
schema:
type: "string"
404:
description: "no such container"
Copy link
Contributor

Choose a reason for hiding this comment

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

should probably be task ?

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'm correcting it but it's also wrong in the docs for service. i need to, in a separate PR, fix the problems with all the API documentation.

@dperny dperny force-pushed the service-logs-support-task-logs branch from 0765e98 to c5d1179 Compare March 31, 2017 23:41
Copy link
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

LGTM 🐸
/cc @thaJeztah @dnephin

return errors.New("logs only supported on container tasks")
}
tty = tty || c.TTY
swarmSelector.TaskIDs = append(swarmSelector.TaskIDs, task.ID)
Copy link
Member

Choose a reason for hiding this comment

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

Could this be a new function convertSelector() ? The existing function is already way too long, so if we could avoid adding more code to it, that would be awesome.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The refactoring in #32154 substantially shortens this function as well, but I'm going to make this change right now anyway.

name := "TestServicelogsTaskLogs"
replicas := 2

out, err := d.Cmd(
Copy link
Member

Choose a reason for hiding this comment

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

result := d.Command(...)
result.Assert(c, icmd.Expected{
    Out: "<something you expect to see in stdout>",
})

will give much better error messages when there are failures here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't exactly work in this place b/c I'm expecting a service ID, which I can't do a simple string match on. But I understand the gist of what you're saying, I'll fix it.

"busybox", "sh", "-c", "for line in $(seq 0 5); do echo $TASK log test $line; done; sleep 100000",
)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
Copy link
Member

Choose a reason for hiding this comment

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

I think this could easily pass when you don't expect it to. out is actually Combined(), so any error message would allow this to pass. Using icmd.Expected{Out: ...} is a much stricter assertion.


// get the task ids
out, err = d.Cmd("service", "ps", "-q", name)
c.Assert(err, checker.IsNil)
Copy link
Member

Choose a reason for hiding this comment

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

same as above (d.Command(...))

for _, taskID := range taskIDs {
c.Logf("checking task %v", taskID)
out, err := d.Cmd("service", "logs", taskID)
c.Assert(err, checker.IsNil, check.Commentf("output: %v", out))
Copy link
Member

Choose a reason for hiding this comment

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

same as above (d.Command(...))

}

taskFormatter := newTaskFormatter(client, opts, padding)
taskFormatter := newTaskFormatter(cli, opts, padding)
Copy link
Member

Choose a reason for hiding this comment

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

I believe this will panic in the formatter if passed a task id where the task slot > 9 because strings.Repeat() will be called with a negative number. padding needs to be set to something reasonable for the tasks case.

padding is a bad name for this variable. It should probably be changed to something like maxLength.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, it does panic. This whole part of the CLI is kind of a mess and I'm going to refactor it later. For now, I'm going to add a check as a bit of a kludge.

Copy link
Member

Choose a reason for hiding this comment

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

Since you have task in the branch above (returned from TaskInspectWithRaw()), couldn't you set padding to

padding = len(strconv.FormatInt(int64(task.Slot), 10))

which would now be duplicated with the formatting below. but could be extracted to a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, I've fixed it correctly.

@dperny dperny force-pushed the service-logs-support-task-logs branch 2 times, most recently from a01f028 to 0ea9867 Compare April 3, 2017 19:50
@dperny
Copy link
Contributor Author

dperny commented Apr 3, 2017

@dnephin fixed concerns.

@dperny dperny force-pushed the service-logs-support-task-logs branch from 0ea9867 to 0131057 Compare April 3, 2017 21:32
@dperny
Copy link
Contributor Author

dperny commented Apr 3, 2017

@dnephin actually fixed all the concerns the right way this time.

Copy link
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

LGTM!

@vieux
Copy link
Contributor

vieux commented Apr 4, 2017

@dperny I think the error is legit

Refactored the API to more easily accept new endpoints. Added REST,
client, and CLI endpoints for getting logs from a specific task. All
that is needed after this commit to enable arbitrary service log
selectors is a REST endpoint and handler.

Task logs can be retrieved by putting in a task ID at the CLI instead of
a service ID.

Signed-off-by: Drew Erny <drew.erny@docker.com>
@dperny dperny force-pushed the service-logs-support-task-logs branch from 0131057 to d330dc3 Compare April 4, 2017 01:41
@dperny
Copy link
Contributor Author

dperny commented Apr 4, 2017

Forgot to strip a trailing newline, it's fixed now.

@vieux
Copy link
Contributor

vieux commented Apr 4, 2017

LGTM 👍

@thaJeztah
Copy link
Member

ping @dperny erm;

Added docker service logs --task command and /task/{id}/logs REST endpoint.

Looks like the --task flag is not part of this PR, correct?

Also, can you open a pull request to add a mention of this endpoint to the API changelog; https://github.com/docker/docker/blob/master/docs/api/version-history.md#v129-api-changes ?

return err
}
maxLength = getMaxLength(task.Slot)
responseBody, err = cli.TaskLogs(ctx, opts.target, options)
Copy link
Member

Choose a reason for hiding this comment

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

Do we have to make this API-version dependent? Or is it ok to have this fail when talking to an older daemon (given that it was "experimental" so far?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh hmmmm I hadn't thought about that...

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 may be mistaken but I BELIEVE if you talk to an older daemon, it's going to fail in the same way it would if I patched this, just with a less helpful error message.

Copy link
Member

Choose a reason for hiding this comment

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

Well, it's still experimental, so "ok" to break, but what happens with a 17.03 / 17.04 client talking to a 17.05 daemon?

@dperny
Copy link
Contributor Author

dperny commented Apr 4, 2017

@thaJeztah Yes, the --task part of this was dropped. Sorry about that. I'll edit the original PR message.

@thaJeztah
Copy link
Member

Yes, the --task part of this was dropped. Sorry about that. I'll edit the original PR message.

Thanks! I was confused for a bit, couldn't find the flag LOL

dnephin pushed a commit to dnephin/docker that referenced this pull request Apr 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants