Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d6bd79c

Browse files
committed
Refactor to new events api
Signed-off-by: Josh Horwitz <[email protected]>
1 parent 9e21784 commit d6bd79c

9 files changed

Lines changed: 193 additions & 117 deletions

File tree

cli/command/container/run.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *runOptions
211211
})
212212
}
213213

214-
statusChan, err := waitExitOrRemoved(dockerCli, context.Background(), createResponse.ID, hostConfig.AutoRemove)
215-
if err != nil {
216-
return fmt.Errorf("Error waiting container's exit code: %v", err)
217-
}
214+
statusChan := waitExitOrRemoved(dockerCli, ctx, createResponse.ID, hostConfig.AutoRemove)
218215

219216
//start the container
220217
if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {

cli/command/container/start.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
108108

109109
// 3. We should open a channel for receiving status code of the container
110110
// no matter it's detached, removed on daemon side(--rm) or exit normally.
111-
statusChan, statusErr := waitExitOrRemoved(dockerCli, context.Background(), c.ID, c.HostConfig.AutoRemove)
111+
statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove)
112112
startOptions := types.ContainerStartOptions{
113113
CheckpointID: opts.checkpoint,
114114
}
@@ -117,7 +117,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
117117
if err := dockerCli.Client().ContainerStart(ctx, c.ID, startOptions); err != nil {
118118
cancelFun()
119119
<-cErr
120-
if c.HostConfig.AutoRemove && statusErr == nil {
120+
if c.HostConfig.AutoRemove {
121121
// wait container to be removed
122122
<-statusChan
123123
}
@@ -134,10 +134,6 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
134134
return attchErr
135135
}
136136

137-
if statusErr != nil {
138-
return fmt.Errorf("can't get container's exit code: %v", statusErr)
139-
}
140-
141137
if status := <-statusChan; status != 0 {
142138
return cli.StatusError{StatusCode: status}
143139
}

cli/command/container/stats.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,22 @@ func runStats(dockerCli *command.DockerCli, opts *statsOptions) error {
6363
options := types.EventsOptions{
6464
Filters: f,
6565
}
66-
resBody, err := dockerCli.Client().Events(ctx, options)
67-
// Whether we successfully subscribed to events or not, we can now
66+
67+
eventq, errq := dockerCli.Client().Events(ctx, options)
68+
69+
// Whether we successfully subscribed to eventq or not, we can now
6870
// unblock the main goroutine.
6971
close(started)
70-
if err != nil {
71-
closeChan <- err
72-
return
73-
}
74-
defer resBody.Close()
7572

76-
system.DecodeEvents(resBody, func(event events.Message, err error) error {
77-
if err != nil {
73+
for {
74+
select {
75+
case event := <-eventq:
76+
c <- event
77+
case err := <-errq:
7878
closeChan <- err
79-
return nil
79+
return
8080
}
81-
c <- event
82-
return nil
83-
})
81+
}
8482
}
8583

8684
// waitFirst is a WaitGroup to wait first stat data's reach for each container

cli/command/container/utils.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package container
22

33
import (
4-
"fmt"
54
"strconv"
65

76
"golang.org/x/net/context"
@@ -11,11 +10,10 @@ import (
1110
"github.com/docker/docker/api/types/events"
1211
"github.com/docker/docker/api/types/filters"
1312
"github.com/docker/docker/cli/command"
14-
"github.com/docker/docker/cli/command/system"
1513
clientapi "github.com/docker/docker/client"
1614
)
1715

18-
func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, containerID string, waitRemove bool) (chan int, error) {
16+
func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, containerID string, waitRemove bool) chan int {
1917
if len(containerID) == 0 {
2018
// containerID can never be empty
2119
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
@@ -24,11 +22,7 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
2422
statusChan := make(chan int)
2523
exitCode := 125
2624

27-
eventProcessor := func(e events.Message, err error) error {
28-
if err != nil {
29-
statusChan <- exitCode
30-
return fmt.Errorf("failed to decode event: %v", err)
31-
}
25+
eventProcessor := func(e events.Message) bool {
3226

3327
stopProcessing := false
3428
switch e.Status {
@@ -53,11 +47,10 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
5347

5448
if stopProcessing {
5549
statusChan <- exitCode
56-
// stop the loop processing
57-
return fmt.Errorf("done")
50+
return true
5851
}
5952

60-
return nil
53+
return false
6154
}
6255

6356
// Get events via Events API
@@ -67,14 +60,29 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
6760
options := types.EventsOptions{
6861
Filters: f,
6962
}
70-
resBody, err := dockerCli.Client().Events(ctx, options)
71-
if err != nil {
72-
return nil, fmt.Errorf("can't get events from daemon: %v", err)
73-
}
7463

75-
go system.DecodeEvents(resBody, eventProcessor)
64+
eventCtx, cancel := context.WithCancel(ctx)
65+
eventq, errq := dockerCli.Client().Events(eventCtx, options)
66+
67+
go func() {
68+
defer cancel()
69+
70+
for {
71+
select {
72+
case evt := <-eventq:
73+
if eventProcessor(evt) {
74+
return
75+
}
76+
77+
case err := <-errq:
78+
logrus.Errorf("error getting events from daemon: %v", err)
79+
statusChan <- exitCode
80+
return
81+
}
82+
}
83+
}()
7684

77-
return statusChan, nil
85+
return statusChan
7886
}
7987

8088
// getExitCode performs an inspect on the container. It returns

cli/command/system/events.go

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,33 @@ func runEvents(dockerCli *command.DockerCli, opts *eventsOptions) error {
6363
Filters: opts.filter.Value(),
6464
}
6565

66-
responseBody, err := dockerCli.Client().Events(context.Background(), options)
67-
if err != nil {
68-
return err
66+
ctx, cancel := context.WithCancel(context.Background())
67+
events, errs := dockerCli.Client().Events(ctx, options)
68+
defer cancel()
69+
70+
out := dockerCli.Out()
71+
72+
for {
73+
select {
74+
case event := <-events:
75+
if err := handleEvent(out, event, tmpl); err != nil {
76+
return err
77+
}
78+
case err := <-errs:
79+
if err == io.EOF {
80+
return nil
81+
}
82+
return err
83+
}
84+
}
85+
}
86+
87+
func handleEvent(out io.Writer, event eventtypes.Message, tmpl *template.Template) error {
88+
if tmpl == nil {
89+
return prettyPrintEvent(out, event)
6990
}
70-
defer responseBody.Close()
7191

72-
return streamEvents(dockerCli.Out(), responseBody, tmpl)
92+
return formatEvent(out, event, tmpl)
7393
}
7494

7595
func makeTemplate(format string) (*template.Template, error) {
@@ -85,21 +105,6 @@ func makeTemplate(format string) (*template.Template, error) {
85105
return tmpl, tmpl.Execute(ioutil.Discard, &eventtypes.Message{})
86106
}
87107

88-
// streamEvents decodes prints the incoming events in the provided output.
89-
func streamEvents(out io.Writer, input io.Reader, tmpl *template.Template) error {
90-
return DecodeEvents(input, func(event eventtypes.Message, err error) error {
91-
if err != nil {
92-
return err
93-
}
94-
if tmpl == nil {
95-
return prettyPrintEvent(out, event)
96-
}
97-
return formatEvent(out, event, tmpl)
98-
})
99-
}
100-
101-
type eventProcessor func(event eventtypes.Message, err error) error
102-
103108
// prettyPrintEvent prints all types of event information.
104109
// Each output includes the event type, actor id, name and action.
105110
// Actor attributes are printed at the end if the actor has any.

cli/command/system/events_utils.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package system
22

33
import (
4-
"encoding/json"
5-
"io"
64
"sync"
75

86
"github.com/Sirupsen/logrus"
97
eventtypes "github.com/docker/docker/api/types/events"
108
)
119

10+
type eventProcessor func(eventtypes.Message, error) error
11+
1212
// EventHandler is abstract interface for user to customize
1313
// own handle functions of each type of events
1414
type EventHandler interface {
@@ -47,20 +47,3 @@ func (w *eventHandler) Watch(c <-chan eventtypes.Message) {
4747
go h(e)
4848
}
4949
}
50-
51-
// DecodeEvents decodes event from input stream
52-
func DecodeEvents(input io.Reader, ep eventProcessor) error {
53-
dec := json.NewDecoder(input)
54-
for {
55-
var event eventtypes.Message
56-
err := dec.Decode(&event)
57-
if err != nil && err == io.EOF {
58-
break
59-
}
60-
61-
if procErr := ep(event, err); procErr != nil {
62-
return procErr
63-
}
64-
}
65-
return nil
66-
}

client/events.go

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,71 @@
11
package client
22

33
import (
4-
"io"
4+
"encoding/json"
55
"net/url"
66
"time"
77

88
"golang.org/x/net/context"
99

1010
"github.com/docker/docker/api/types"
11+
"github.com/docker/docker/api/types/events"
1112
"github.com/docker/docker/api/types/filters"
1213
timetypes "github.com/docker/docker/api/types/time"
1314
)
1415

15-
// Events returns a stream of events in the daemon in a ReadCloser.
16-
// It's up to the caller to close the stream.
17-
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error) {
16+
// Events returns a stream of events in the daemon. It's up to the caller to close the stream
17+
// by cancelling the context. Once the stream has been completely read an io.EOF error will
18+
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
19+
// to the caller to reopen the stream in the event of an error by reinvoking this method.
20+
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) {
21+
22+
messages := make(chan events.Message)
23+
errs := make(chan error, 1)
24+
25+
go func() {
26+
defer close(errs)
27+
28+
query, err := buildEventsQueryParams(cli.version, options)
29+
if err != nil {
30+
errs <- err
31+
return
32+
}
33+
34+
resp, err := cli.get(ctx, "/events", query, nil)
35+
if err != nil {
36+
errs <- err
37+
return
38+
}
39+
defer resp.body.Close()
40+
41+
decoder := json.NewDecoder(resp.body)
42+
43+
for {
44+
select {
45+
case <-ctx.Done():
46+
errs <- ctx.Err()
47+
return
48+
default:
49+
var event events.Message
50+
if err := decoder.Decode(&event); err != nil {
51+
errs <- err
52+
return
53+
}
54+
55+
select {
56+
case messages <- event:
57+
case <-ctx.Done():
58+
errs <- ctx.Err()
59+
return
60+
}
61+
}
62+
}
63+
}()
64+
65+
return messages, errs
66+
}
67+
68+
func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url.Values, error) {
1869
query := url.Values{}
1970
ref := time.Now()
2071

@@ -25,24 +76,22 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.
2576
}
2677
query.Set("since", ts)
2778
}
79+
2880
if options.Until != "" {
2981
ts, err := timetypes.GetTimestamp(options.Until, ref)
3082
if err != nil {
3183
return nil, err
3284
}
3385
query.Set("until", ts)
3486
}
87+
3588
if options.Filters.Len() > 0 {
36-
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
89+
filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters)
3790
if err != nil {
3891
return nil, err
3992
}
4093
query.Set("filters", filterJSON)
4194
}
4295

43-
serverResponse, err := cli.get(ctx, "/events", query, nil)
44-
if err != nil {
45-
return nil, err
46-
}
47-
return serverResponse.body, nil
96+
return query, nil
4897
}

0 commit comments

Comments
 (0)