Merge pull request #18888 from calavera/event_types

Event all the things!
Upstream-commit: 723be0a3325799fd6b2a6b689af54f5a07edf992
Component: engine
This commit is contained in:
David Calavera
2016-01-04 13:07:33 -08:00
40 changed files with 1314 additions and 561 deletions
+60 -2
View File
@@ -1,11 +1,18 @@
package client
import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/docker/docker/api/types"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/jsonlog"
flag "github.com/docker/docker/pkg/mflag"
)
@@ -46,5 +53,56 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
}
defer responseBody.Close()
return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut)
return streamEvents(responseBody, cli.out)
}
// streamEvents decodes prints the incoming events in the provided output.
func streamEvents(input io.Reader, output io.Writer) error {
return decodeEvents(input, func(event eventtypes.Message, err error) error {
if err != nil {
return err
}
printOutput(event, output)
return nil
})
}
type eventProcessor func(event eventtypes.Message, err error) error
func decodeEvents(input io.Reader, ep eventProcessor) error {
dec := json.NewDecoder(input)
for {
var event eventtypes.Message
err := dec.Decode(&event)
if err != nil && err == io.EOF {
break
}
if procErr := ep(event, err); procErr != nil {
return procErr
}
}
return nil
}
// printOutput prints all types of event information.
// Each output includes the event type, actor id, name and action.
// Actor attributes are printed at the end if the actor has any.
func printOutput(event eventtypes.Message, output io.Writer) {
if event.TimeNano != 0 {
fmt.Fprintf(output, "%s ", time.Unix(0, event.TimeNano).Format(jsonlog.RFC3339NanoFixed))
} else if event.Time != 0 {
fmt.Fprintf(output, "%s ", time.Unix(event.Time, 0).Format(jsonlog.RFC3339NanoFixed))
}
fmt.Fprintf(output, "%s %s %s", event.Type, event.Action, event.Actor.ID)
if len(event.Actor.Attributes) > 0 {
var attrs []string
for k, v := range event.Actor.Attributes {
attrs = append(attrs, fmt.Sprintf("%s=%s", k, v))
}
fmt.Fprintf(output, " (%s)", strings.Join(attrs, ", "))
}
fmt.Fprint(output, "\n")
}
+14 -9
View File
@@ -11,8 +11,9 @@ import (
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/go-units"
)
@@ -189,7 +190,11 @@ func (cli *DockerCli) CmdStats(args ...string) error {
err error
}
getNewContainers := func(c chan<- watch) {
options := types.EventsOptions{}
f := filters.NewArgs()
f.Add("type", "container")
options := types.EventsOptions{
Filters: f,
}
resBody, err := cli.client.Events(options)
if err != nil {
c <- watch{err: err}
@@ -197,15 +202,15 @@ func (cli *DockerCli) CmdStats(args ...string) error {
}
defer resBody.Close()
dec := json.NewDecoder(resBody)
for {
var j *jsonmessage.JSONMessage
if err := dec.Decode(&j); err != nil {
decodeEvents(resBody, func(event events.Message, err error) error {
if err != nil {
c <- watch{err: err}
return
return nil
}
c <- watch{j.ID[:12], j.Status, nil}
}
c <- watch{event.ID[:12], event.Action, nil}
return nil
})
}
go func(stopChan chan<- error) {
cChan := make(chan watch)
@@ -19,4 +19,5 @@ type Backend interface {
DisconnectContainerFromNetwork(containerName string,
network libnetwork.Network) error
NetworkControllerEnabled() bool
DeleteNetwork(name string) error
}
@@ -148,21 +148,7 @@ func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.Respon
}
func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
nw, err := n.backend.FindNetwork(vars["id"])
if err != nil {
return err
}
if runconfig.IsPreDefinedNetwork(nw.Name()) {
return httputils.WriteJSON(w, http.StatusForbidden,
fmt.Sprintf("%s is a pre-defined network and cannot be removed", nw.Name()))
}
return nw.Delete()
return n.backend.DeleteNetwork(vars["id"])
}
func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
@@ -2,8 +2,8 @@ package system
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/jsonmessage"
)
// Backend is the methods that need to be implemented to provide
@@ -11,7 +11,7 @@ import (
type Backend interface {
SystemInfo() (*types.Info, error)
SystemVersion() types.Version
SubscribeToEvents(since, sinceNano int64, ef filters.Args) ([]*jsonmessage.JSONMessage, chan interface{})
SubscribeToEvents(since, sinceNano int64, ef filters.Args) ([]events.Message, chan interface{})
UnsubscribeFromEvents(chan interface{})
AuthenticateToRegistry(authConfig *types.AuthConfig) (string, error)
}
@@ -9,10 +9,10 @@ import (
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
timetypes "github.com/docker/docker/api/types/time"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/jsonmessage"
"golang.org/x/net/context"
)
@@ -98,8 +98,9 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
for {
select {
case ev := <-l:
jev, ok := ev.(*jsonmessage.JSONMessage)
jev, ok := ev.(events.Message)
if !ok {
logrus.Warnf("unexpected event message: %q", ev)
continue
}
if err := enc.Encode(jev); err != nil {
@@ -0,0 +1,38 @@
package events
const (
// ContainerEventType is the event type that containers generate
ContainerEventType = "container"
// ImageEventType is the event type that images generate
ImageEventType = "image"
// VolumeEventType is the event type that volumes generate
VolumeEventType = "volume"
// NetworkEventType is the event type that networks generate
NetworkEventType = "network"
)
// Actor describes something that generates events,
// like a container, or a network, or a volume.
// It has a defined name and a set or attributes.
// The container attributes are its labels, other actors
// can generate these attributes from other properties.
type Actor struct {
ID string
Attributes map[string]string
}
// Message represents the information an event contains
type Message struct {
// Deprecated information from JSONMessage.
// With data only in container events.
Status string `json:"status,omitempty"`
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
Type string
Action string
Actor Actor
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
}
@@ -197,6 +197,22 @@ func (filters Args) ExactMatch(field, source string) bool {
return false
}
// FuzzyMatch returns true if the source matches exactly one of the filters,
// or the source has one of the filters as a prefix.
func (filters Args) FuzzyMatch(field, source string) bool {
if filters.ExactMatch(field, source) {
return true
}
fieldValues := filters.fields[field]
for prefix := range fieldValues {
if strings.HasPrefix(source, prefix) {
return true
}
}
return false
}
// Include returns true if the name of the field to filter is in the filters.
func (filters Args) Include(field string) bool {
_, ok := filters.fields[field]
@@ -349,3 +349,21 @@ func TestWalkValues(t *testing.T) {
t.Fatalf("Expected to not iterate when the field doesn't exist, got %v", err)
}
}
func TestFuzzyMatch(t *testing.T) {
f := NewArgs()
f.Add("container", "foo")
cases := map[string]bool{
"foo": true,
"foobar": true,
"barfoo": false,
"bar": false,
}
for source, match := range cases {
got := f.FuzzyMatch("container", source)
if got != match {
t.Fatalf("Expected %v, got %v: %s", match, got, source)
}
}
}