From ed916a233c577693afc8dc3122311c15246d00cc Mon Sep 17 00:00:00 2001
From: allencloud <allen.sun@daocloud.io>
Date: Mon, 10 Jul 2017 11:41:25 +0800
Subject: [PATCH 1/2] make engine support cluster config event

Signed-off-by: allencloud <allen.sun@daocloud.io>
---
 api/types/events/events.go               |  2 ++
 daemon/cluster/noderunner.go             |  4 ++++
 daemon/events.go                         | 10 ++++++++++
 daemon/events/filter.go                  |  4 ++++
 integration-cli/docker_cli_swarm_test.go | 20 ++++++++++++++++++++
 5 files changed, 40 insertions(+)

diff --git a/api/types/events/events.go b/api/types/events/events.go
index 5f5f540346..e292565b6c 100644
--- a/api/types/events/events.go
+++ b/api/types/events/events.go
@@ -19,6 +19,8 @@ const (
 	NodeEventType = "node"
 	// SecretEventType is the event type that secrets generate
 	SecretEventType = "secret"
+	// ConfigEventType is the event type that configs generate
+	ConfigEventType = "config"
 )
 
 // Actor describes something that generates events,
diff --git a/daemon/cluster/noderunner.go b/daemon/cluster/noderunner.go
index a1eda066b2..b970e7b217 100644
--- a/daemon/cluster/noderunner.go
+++ b/daemon/cluster/noderunner.go
@@ -202,6 +202,10 @@ func (n *nodeRunner) watchClusterEvents(ctx context.Context, conn *grpc.ClientCo
 				Kind:   "secret",
 				Action: swarmapi.WatchActionKindCreate | swarmapi.WatchActionKindUpdate | swarmapi.WatchActionKindRemove,
 			},
+			{
+				Kind:   "config",
+				Action: swarmapi.WatchActionKindCreate | swarmapi.WatchActionKindUpdate | swarmapi.WatchActionKindRemove,
+			},
 		},
 		IncludeOldObject: true,
 	})
diff --git a/daemon/events.go b/daemon/events.go
index f5d188cf0b..7ae851802e 100644
--- a/daemon/events.go
+++ b/daemon/events.go
@@ -175,6 +175,8 @@ func (daemon *Daemon) generateClusterEvent(msg *swarmapi.WatchMessage) {
 			daemon.logNetworkEvent(event.Action, v.Network, event.OldObject.GetNetwork())
 		case *swarmapi.Object_Secret:
 			daemon.logSecretEvent(event.Action, v.Secret, event.OldObject.GetSecret())
+		case *swarmapi.Object_Config:
+			daemon.logConfigEvent(event.Action, v.Config, event.OldObject.GetConfig())
 		default:
 			logrus.Warnf("unrecognized event: %v", event)
 		}
@@ -197,6 +199,14 @@ func (daemon *Daemon) logSecretEvent(action swarmapi.WatchActionKind, secret *sw
 	daemon.logClusterEvent(action, secret.ID, "secret", attributes, eventTime)
 }
 
+func (daemon *Daemon) logConfigEvent(action swarmapi.WatchActionKind, config *swarmapi.Config, oldConfig *swarmapi.Config) {
+	attributes := map[string]string{
+		"name": config.Spec.Annotations.Name,
+	}
+	eventTime := eventTimestamp(config.Meta, action)
+	daemon.logClusterEvent(action, config.ID, "config", attributes, eventTime)
+}
+
 func (daemon *Daemon) logNodeEvent(action swarmapi.WatchActionKind, node *swarmapi.Node, oldNode *swarmapi.Node) {
 	name := node.Spec.Annotations.Name
 	if name == "" && node.Description != nil {
diff --git a/daemon/events/filter.go b/daemon/events/filter.go
index 7f1a5fda1c..645f1ca917 100644
--- a/daemon/events/filter.go
+++ b/daemon/events/filter.go
@@ -94,6 +94,10 @@ func (ef *Filter) matchSecret(ev events.Message) bool {
 	return ef.fuzzyMatchName(ev, events.SecretEventType)
 }
 
+func (ef *Filter) matchConfig(ev events.Message) bool {
+	return ef.fuzzyMatchName(ev, events.ConfigEventType)
+}
+
 func (ef *Filter) fuzzyMatchName(ev events.Message, eventType string) bool {
 	return ef.filter.FuzzyMatch(eventType, ev.Actor.ID) ||
 		ef.filter.FuzzyMatch(eventType, ev.Actor.Attributes["name"])
diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go
index c5625b4f8c..a0bb7a2283 100644
--- a/integration-cli/docker_cli_swarm_test.go
+++ b/integration-cli/docker_cli_swarm_test.go
@@ -2208,3 +2208,23 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *check.C) {
 	// filtered by secret
 	waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount)
 }
+
+func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
+	d := s.AddDaemon(c, true, true)
+
+	testName := "test_config"
+	id := d.CreateConfig(c, swarm.ConfigSpec{
+		Annotations: swarm.Annotations{
+			Name: testName,
+		},
+		Data: []byte("TESTINGDATA"),
+	})
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
+
+	waitForEvent(c, d, "0", "-f scope=swarm", "config create "+id, defaultRetryCount)
+
+	t1 := daemonUnixTime(c)
+	d.DeleteConfig(c, id)
+	// filtered by config
+	waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount)
+}

From c8d6477e5a359d30cdf0a59cdbfffc158a1d9388 Mon Sep 17 00:00:00 2001
From: allencloud <allen.sun@daocloud.io>
Date: Tue, 11 Jul 2017 17:22:46 +0800
Subject: [PATCH 2/2] add config event in swagger.yml

Signed-off-by: allencloud <allen.sun@daocloud.io>
---
 api/swagger.yaml            | 2 ++
 docs/api/version-history.md | 1 +
 2 files changed, 3 insertions(+)

diff --git a/api/swagger.yaml b/api/swagger.yaml
index 75fd20e3e3..7e451167e8 100644
--- a/api/swagger.yaml
+++ b/api/swagger.yaml
@@ -5678,6 +5678,8 @@ paths:
 
         Secrets report these events: `create`, `update`, and `remove`
 
+        Configs report these events: `create`, `update`, and `remove`
+
       operationId: "SystemEvents"
       produces:
         - "application/json"
diff --git a/docs/api/version-history.md b/docs/api/version-history.md
index 69e985af4e..be03752ed6 100644
--- a/docs/api/version-history.md
+++ b/docs/api/version-history.md
@@ -27,6 +27,7 @@ keywords: "API, Docker, rcli, REST, documentation"
   enabled.
 * `GET /images/(name)/get` now includes an `ImageMetadata` field which contains image metadata that is local to the engine and not part of the image config.
 * `POST /services/create` now accepts a `PluginSpec` when `TaskTemplate.Runtime` is set to `plugin`
+* `GET /events` now supports config events `create`, `update` and `remove` that are emitted when users create, update or remove a config
 
 ## v1.30 API changes