add network events
This commit is contained in:
parent
c5bea32e01
commit
099c635aa7
36
events/container.go
Normal file
36
events/container.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
dockerEvents "github.com/docker/docker/api/types/events"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Action string
|
||||||
|
ID string
|
||||||
|
Container *types.ContainerJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
//goland:noinspection ALL
|
||||||
|
func (c Container) __interface_event() {
|
||||||
|
panic("interface event guard")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContainer(message dockerEvents.Message, client *client.Client) Event {
|
||||||
|
var e Container
|
||||||
|
e.ID = message.Actor.ID
|
||||||
|
e.Action = message.Action
|
||||||
|
|
||||||
|
if message.Action != "destroy" {
|
||||||
|
container, err := client.ContainerInspect(context.TODO(), message.Actor.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error inspecting container %v: %v", message.Actor.ID, err)
|
||||||
|
} else {
|
||||||
|
e.Container = &container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
24
events/event.go
Normal file
24
events/event.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
dockerEvents "github.com/docker/docker/api/types/events"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
//goland:noinspection GoSnakeCaseUsage
|
||||||
|
type Event interface {
|
||||||
|
__interface_event()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Make(message dockerEvents.Message, client *client.Client) Event {
|
||||||
|
switch message.Type {
|
||||||
|
case "container":
|
||||||
|
return makeContainer(message, client)
|
||||||
|
case "network":
|
||||||
|
return makeNetwork(message, client)
|
||||||
|
default:
|
||||||
|
log.Printf("unknown event type %v", message.Type)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
48
events/network.go
Normal file
48
events/network.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
dockerEvents "github.com/docker/docker/api/types/events"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
Action string
|
||||||
|
ID string
|
||||||
|
Network *types.NetworkResource
|
||||||
|
Container *types.ContainerJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
//goland:noinspection ALL
|
||||||
|
func (c Network) __interface_event() {
|
||||||
|
panic("interface event guard")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNetwork(message dockerEvents.Message, client *client.Client) Event {
|
||||||
|
var e Network
|
||||||
|
e.ID = message.Actor.ID
|
||||||
|
e.Action = message.Action
|
||||||
|
|
||||||
|
if message.Action != "destroy" {
|
||||||
|
network, err := client.NetworkInspect(context.TODO(), message.Actor.ID, types.NetworkInspectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error inspecting network %v: %v", message.Actor.ID, err)
|
||||||
|
} else {
|
||||||
|
e.Network = &network
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Action == "connect" || message.Action == "disconnect" {
|
||||||
|
if containerId, ok := message.Actor.Attributes["container"]; ok {
|
||||||
|
container, err := client.ContainerInspect(context.TODO(), containerId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error inspecting container %v: %v", containerId, err)
|
||||||
|
} else {
|
||||||
|
e.Container = &container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
17
example.conf
17
example.conf
|
@ -1,7 +1,12 @@
|
||||||
[Handler]
|
|
||||||
Action=start
|
|
||||||
Run=notify-send "docker container event: start, name={{ .Name }}"
|
|
||||||
|
|
||||||
[Handler]
|
[Container]
|
||||||
Action=die
|
Action=start
|
||||||
Run=notify-send "docker container event: die, name={{ .Name }}"
|
Run=notify-send "{{ .Container.Name }}"
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
Action=connect
|
||||||
|
Run=notify-send "connected container {{ .Container.Name }} to network {{ .Network.Name }}"
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
Action=disconnect
|
||||||
|
Run=notify-send "disconnected container {{ .Container.Name }} from network {{ .Network.Name }}"
|
||||||
|
|
112
handlers/container.go
Normal file
112
handlers/container.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"docker-event-handler/events"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Action []string
|
||||||
|
Name []string
|
||||||
|
Image []string
|
||||||
|
ID []string
|
||||||
|
Hostname []string
|
||||||
|
Label map[string]*string
|
||||||
|
Run *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Container) Matches(event events.Event) bool {
|
||||||
|
e, ok := event.(events.Container)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stringListMatches(h.Action, e.Action) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stringListMatches(h.ID, e.ID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Container == nil {
|
||||||
|
if len(h.Name) > 0 || len(h.Image) > 0 || len(h.Hostname) > 0 || len(h.Label) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !stringListMatches(h.Name, e.Container.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.Image, e.Container.Image) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.Hostname, e.Container.Config.Hostname) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !labelsMatch(h.Label, e.Container.Config.Labels) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Container) Invoke(event events.Event) error {
|
||||||
|
e := event.(events.Container) // enforce that the event is a container events
|
||||||
|
return runTemplatedScript(h.Run, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readContainerFromConfig(section *ini.Section) (Container, error) {
|
||||||
|
var handler Container
|
||||||
|
var err error
|
||||||
|
handler.Label = make(map[string]*string)
|
||||||
|
for _, key := range section.Keys() {
|
||||||
|
val := key.String()
|
||||||
|
switch key.Name() {
|
||||||
|
case "Action":
|
||||||
|
handler.Action = append(handler.Action, val)
|
||||||
|
break
|
||||||
|
case "Name":
|
||||||
|
handler.Name = append(handler.Name, val)
|
||||||
|
break
|
||||||
|
case "Image":
|
||||||
|
handler.Image = append(handler.Image, val)
|
||||||
|
break
|
||||||
|
case "ID":
|
||||||
|
handler.ID = append(handler.ID, val)
|
||||||
|
break
|
||||||
|
case "Hostname":
|
||||||
|
handler.Hostname = append(handler.Hostname, val)
|
||||||
|
break
|
||||||
|
case "Label":
|
||||||
|
parts := strings.SplitN(val, "=", 2)
|
||||||
|
var k string
|
||||||
|
var v *string
|
||||||
|
if len(parts) == 2 {
|
||||||
|
k = parts[0]
|
||||||
|
v = &parts[1]
|
||||||
|
} else {
|
||||||
|
k = parts[0]
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
handler.Label[k] = v
|
||||||
|
break
|
||||||
|
case "Run":
|
||||||
|
if handler.Run != nil {
|
||||||
|
return handler, errors.New(fmt.Sprintf("duplicate key %v in section %v", key.Name(), section.Name()))
|
||||||
|
}
|
||||||
|
handler.Run, err = template.New("Run").Parse(val)
|
||||||
|
if err != nil {
|
||||||
|
return handler, err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return handler, errors.New(fmt.Sprintf("unknown key %v in section %v", key.Name(), section.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler, nil
|
||||||
|
}
|
76
handlers/handler.go
Normal file
76
handlers/handler.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"docker-event-handler/events"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/shlex"
|
||||||
|
"github.com/ryanuber/go-glob"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"os/exec"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadFromConfig(section *ini.Section) (Handler, error) {
|
||||||
|
switch section.Name() {
|
||||||
|
case "Container":
|
||||||
|
return readContainerFromConfig(section)
|
||||||
|
case "Network":
|
||||||
|
return readNetworkFromConfig(section)
|
||||||
|
default:
|
||||||
|
return nil, errors.New(fmt.Sprintf("unknown section %v", section.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Matches(event events.Event) bool
|
||||||
|
Invoke(event events.Event) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringListMatches(list []string, subject string) bool {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, pattern := range list {
|
||||||
|
if glob.Glob(pattern, subject) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func labelsMatch(want map[string]*string, has map[string]string) bool {
|
||||||
|
for key, val := range want {
|
||||||
|
actualVal, exists := has[key]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if val != nil {
|
||||||
|
if actualVal != *val {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTemplatedScript(template *template.Template, data interface{}) error {
|
||||||
|
var cmdBuf bytes.Buffer
|
||||||
|
err := template.Execute(&cmdBuf, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parts, err := shlex.Split(cmdBuf.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) < 1 {
|
||||||
|
return errors.New("no handler given")
|
||||||
|
}
|
||||||
|
name := parts[0]
|
||||||
|
args := parts[1:]
|
||||||
|
|
||||||
|
return exec.Command(name, args...).Run()
|
||||||
|
}
|
159
handlers/network.go
Normal file
159
handlers/network.go
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"docker-event-handler/events"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
Action []string
|
||||||
|
Name []string
|
||||||
|
Driver []string
|
||||||
|
ID []string
|
||||||
|
Label map[string]*string
|
||||||
|
ContainerName []string
|
||||||
|
ContainerImage []string
|
||||||
|
ContainerID []string
|
||||||
|
ContainerHostname []string
|
||||||
|
ContainerLabel map[string]*string
|
||||||
|
Run *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Network) Matches(event events.Event) bool {
|
||||||
|
e, ok := event.(events.Network)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stringListMatches(h.Action, e.Action) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stringListMatches(h.ID, e.ID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Network == nil {
|
||||||
|
if len(h.Name) > 0 || len(h.Driver) > 0 || len(h.Label) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !stringListMatches(h.Name, e.Network.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.Driver, e.Network.Driver) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !labelsMatch(h.Label, e.Network.Labels) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Container == nil {
|
||||||
|
if len(h.ContainerID) > 0 || len(h.ContainerName) > 0 || len(h.ContainerImage) > 0 || len(h.ContainerHostname) > 0 || len(h.ContainerLabel) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !stringListMatches(h.ContainerID, e.Container.ID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.ContainerName, e.Container.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.ContainerImage, e.Container.Image) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !stringListMatches(h.ContainerHostname, e.Container.Config.Hostname) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !labelsMatch(h.ContainerLabel, e.Container.Config.Labels) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Network) Invoke(event events.Event) error {
|
||||||
|
e := event.(events.Network) // enforce that the event is a network event
|
||||||
|
return runTemplatedScript(h.Run, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNetworkFromConfig(section *ini.Section) (Network, error) {
|
||||||
|
var handler Network
|
||||||
|
var err error
|
||||||
|
handler.Label = make(map[string]*string)
|
||||||
|
handler.ContainerLabel = make(map[string]*string)
|
||||||
|
for _, key := range section.Keys() {
|
||||||
|
val := key.String()
|
||||||
|
switch key.Name() {
|
||||||
|
case "Action":
|
||||||
|
handler.Action = append(handler.Action, val)
|
||||||
|
break
|
||||||
|
case "Name":
|
||||||
|
handler.Name = append(handler.Name, val)
|
||||||
|
break
|
||||||
|
case "ID":
|
||||||
|
handler.ID = append(handler.ID, val)
|
||||||
|
break
|
||||||
|
case "Driver":
|
||||||
|
handler.Driver = append(handler.Driver, val)
|
||||||
|
break
|
||||||
|
case "Label":
|
||||||
|
parts := strings.SplitN(val, "=", 2)
|
||||||
|
var k string
|
||||||
|
var v *string
|
||||||
|
if len(parts) == 2 {
|
||||||
|
k = parts[0]
|
||||||
|
v = &parts[1]
|
||||||
|
} else {
|
||||||
|
k = parts[0]
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
handler.Label[k] = v
|
||||||
|
break
|
||||||
|
|
||||||
|
case "ContainerName":
|
||||||
|
handler.ContainerName = append(handler.ContainerName, val)
|
||||||
|
break
|
||||||
|
case "ContainerID":
|
||||||
|
handler.ContainerID = append(handler.ContainerID, val)
|
||||||
|
break
|
||||||
|
case "ContainerImage":
|
||||||
|
handler.ContainerImage = append(handler.ContainerImage, val)
|
||||||
|
break
|
||||||
|
case "ContainerHostname":
|
||||||
|
handler.ContainerHostname = append(handler.ContainerHostname, val)
|
||||||
|
break
|
||||||
|
case "ContainerLabel":
|
||||||
|
parts := strings.SplitN(val, "=", 2)
|
||||||
|
var k string
|
||||||
|
var v *string
|
||||||
|
if len(parts) == 2 {
|
||||||
|
k = parts[0]
|
||||||
|
v = &parts[1]
|
||||||
|
} else {
|
||||||
|
k = parts[0]
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
handler.ContainerLabel[k] = v
|
||||||
|
break
|
||||||
|
case "Run":
|
||||||
|
if handler.Run != nil {
|
||||||
|
return handler, errors.New(fmt.Sprintf("duplicate key %v in section %v", key.Name(), section.Name()))
|
||||||
|
}
|
||||||
|
handler.Run, err = template.New("Run").Parse(val)
|
||||||
|
if err != nil {
|
||||||
|
return handler, err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return handler, errors.New(fmt.Sprintf("unknown key %v in section %v", key.Name(), section.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler, nil
|
||||||
|
}
|
188
main.go
188
main.go
|
@ -1,147 +1,37 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"docker-event-handler/events"
|
||||||
"fmt"
|
"docker-event-handler/handlers"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/events"
|
dockerEvents "github.com/docker/docker/api/types/events"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/google/shlex"
|
|
||||||
"github.com/ryanuber/go-glob"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
handlers []Handler
|
handlers []handlers.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerEvent struct {
|
func handleEvent(dockerClient *client.Client, event dockerEvents.Message, handlers []handlers.Handler) {
|
||||||
Action string
|
e := events.Make(event, dockerClient)
|
||||||
Name string
|
|
||||||
ID string
|
|
||||||
Image string
|
|
||||||
Labels map[string]string
|
|
||||||
Mounts []types.MountPoint
|
|
||||||
Hostname string
|
|
||||||
IPAddress string
|
|
||||||
Ports nat.PortMap
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handler struct {
|
if e == nil {
|
||||||
Action []string
|
|
||||||
Name []string
|
|
||||||
Image []string
|
|
||||||
ID []string
|
|
||||||
Hostname []string
|
|
||||||
Label map[string]*string
|
|
||||||
Handler *template.Template
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Handler) matches(e ContainerEvent) bool {
|
|
||||||
if !stringListMatches(h.Action, e.Action) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !stringListMatches(h.Name, e.Name) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !stringListMatches(h.Image, e.Image) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !stringListMatches(h.ID, e.ID) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !stringListMatches(h.Hostname, e.Hostname) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, val := range h.Label {
|
|
||||||
actualVal, exists := e.Labels[key]
|
|
||||||
if !exists {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if val != nil {
|
|
||||||
if actualVal != *val {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Handler) invoke(e ContainerEvent) error {
|
|
||||||
var cmdBuf bytes.Buffer
|
|
||||||
err := h.Handler.Execute(&cmdBuf, e)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
parts, err := shlex.Split(cmdBuf.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) < 1 {
|
|
||||||
return errors.New("no handler given")
|
|
||||||
}
|
|
||||||
name := parts[0]
|
|
||||||
args := parts[1:]
|
|
||||||
|
|
||||||
return exec.Command(name, args...).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringListMatches(list []string, subject string) bool {
|
|
||||||
if len(list) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, pattern := range list {
|
|
||||||
if glob.Glob(pattern, subject) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleEvent(dockerClient *client.Client, event events.Message, handlers []Handler) {
|
|
||||||
if event.Type != "container" { // only container events are currently supported
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if event.Action == "destroy" { // destroy event is not supported because the container can't be inspected
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := dockerClient.ContainerInspect(context.TODO(), event.Actor.ID)
|
log.Printf("%T%+v", e, e)
|
||||||
if err != nil {
|
|
||||||
log.Printf("error inspecting container %v: %v", event.Actor.ID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ev := ContainerEvent{
|
|
||||||
Action: event.Action,
|
|
||||||
Name: container.Name,
|
|
||||||
ID: event.Actor.ID,
|
|
||||||
Image: container.Config.Image,
|
|
||||||
Labels: container.Config.Labels,
|
|
||||||
Mounts: container.Mounts,
|
|
||||||
Hostname: container.Config.Hostname,
|
|
||||||
IPAddress: container.NetworkSettings.IPAddress,
|
|
||||||
Ports: container.NetworkSettings.Ports,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range handlers {
|
for _, handler := range handlers {
|
||||||
if handler.matches(ev) {
|
if handler.Matches(e) {
|
||||||
err := handler.invoke(ev)
|
err := handler.Invoke(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error invoking handler: %v", err)
|
log.Printf("error invoking handler: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -160,55 +50,14 @@ func readConfiguration(path string) (Configuration, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, section := range cfg.Sections() {
|
for _, section := range cfg.Sections() {
|
||||||
if section.Name() == "Handler" {
|
if section.Name() == "DEFAULT" {
|
||||||
var handler Handler
|
continue
|
||||||
handler.Label = make(map[string]*string)
|
|
||||||
for _, key := range section.Keys() {
|
|
||||||
val := key.String()
|
|
||||||
switch key.Name() {
|
|
||||||
case "Action":
|
|
||||||
handler.Action = append(handler.Action, val)
|
|
||||||
break
|
|
||||||
case "Name":
|
|
||||||
handler.Name = append(handler.Name, val)
|
|
||||||
break
|
|
||||||
case "Image":
|
|
||||||
handler.Image = append(handler.Image, val)
|
|
||||||
break
|
|
||||||
case "ID":
|
|
||||||
handler.ID = append(handler.ID, val)
|
|
||||||
break
|
|
||||||
case "Hostname":
|
|
||||||
handler.Hostname = append(handler.Hostname, val)
|
|
||||||
break
|
|
||||||
case "Label":
|
|
||||||
parts := strings.SplitN(val, "=", 2)
|
|
||||||
var k string
|
|
||||||
var v *string
|
|
||||||
if len(parts) == 2 {
|
|
||||||
k = parts[0]
|
|
||||||
v = &parts[1]
|
|
||||||
} else {
|
|
||||||
k = parts[0]
|
|
||||||
v = nil
|
|
||||||
}
|
|
||||||
handler.Label[k] = v
|
|
||||||
break
|
|
||||||
case "Run":
|
|
||||||
if handler.Handler != nil {
|
|
||||||
return config, errors.New(fmt.Sprintf("duplicate key %v in section %v", key.Name(), section.Name()))
|
|
||||||
}
|
|
||||||
handler.Handler, err = template.New("Handler").Parse(val)
|
|
||||||
if err != nil {
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return config, errors.New(fmt.Sprintf("unknown key %v in section %v", key.Name(), section.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.handlers = append(config.handlers, handler)
|
|
||||||
}
|
}
|
||||||
|
handler, err := handlers.ReadFromConfig(section)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
config.handlers = append(config.handlers, handler)
|
||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@ -236,7 +85,8 @@ func main() {
|
||||||
evs, errs := dockerClient.Events(context.TODO(), types.EventsOptions{
|
evs, errs := dockerClient.Events(context.TODO(), types.EventsOptions{
|
||||||
Since: strconv.FormatInt(time.Now().Unix(), 10),
|
Since: strconv.FormatInt(time.Now().Unix(), 10),
|
||||||
Filters: filters.NewArgs(
|
Filters: filters.NewArgs(
|
||||||
filters.Arg("type", events.ContainerEventType),
|
filters.Arg("type", dockerEvents.ContainerEventType),
|
||||||
|
filters.Arg("type", dockerEvents.NetworkEventType),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue