diff --git a/api/server/router/network/backend.go b/api/server/router/network/backend.go index 75bd12c5d8..c3d6101db6 100644 --- a/api/server/router/network/backend.go +++ b/api/server/router/network/backend.go @@ -19,4 +19,5 @@ type Backend interface { DisconnectContainerFromNetwork(containerName string, network libnetwork.Network) error NetworkControllerEnabled() bool + DeleteNetwork(name string) error } diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 6c4e22e7e7..86ef6854c1 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -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 { diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 0b1091a68f..f896912f9e 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -699,6 +699,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName return derr.ErrorCodeJoinInfo.WithArgs(err) } + daemon.LogNetworkEventWithAttributes(n, "connect", map[string]string{"container": container.ID}) return nil } @@ -850,11 +851,15 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) { sid := container.NetworkSettings.SandboxID settings := container.NetworkSettings.Networks + var networks []libnetwork.Network for n := range settings { + if nw, err := daemon.FindNetwork(n); err == nil { + networks = append(networks, nw) + } settings[n] = &networktypes.EndpointSettings{} } - container.NetworkSettings = &network.Settings{Networks: networks} + container.NetworkSettings = &network.Settings{Networks: settings} if sid == "" || len(settings) == 0 { return @@ -873,8 +878,8 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) { attributes := map[string]string{ "container": container.ID, } - for nwID := range settings { - daemon.logNetworkEventWithID(nwID, "disconnect", attributes) + for _, nw := range networks { + daemon.LogNetworkEventWithAttributes(nw, "disconnect", attributes) } } diff --git a/daemon/events.go b/daemon/events.go index e9f9432af4..3211679c10 100644 --- a/daemon/events.go +++ b/daemon/events.go @@ -61,12 +61,8 @@ func (daemon *Daemon) LogNetworkEvent(nw libnetwork.Network, action string) { func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, action string, attributes map[string]string) { attributes["name"] = nw.Name() attributes["type"] = nw.Type() - daemon.logNetworkEventWithID(nw.ID(), action, attributes) -} - -func (daemon *Daemon) logNetworkEventWithID(id, action string, attributes map[string]string) { actor := events.Actor{ - ID: id, + ID: nw.ID(), Attributes: attributes, } daemon.EventsService.Log(action, events.NetworkEventType, actor) diff --git a/daemon/network.go b/daemon/network.go index e803ab1963..a3282517de 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -7,6 +7,8 @@ import ( "strings" "github.com/docker/docker/api/types/network" + derr "github.com/docker/docker/errors" + "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" ) @@ -114,7 +116,13 @@ func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, opti nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf)) nwOptions = append(nwOptions, libnetwork.NetworkOptionDriverOpts(options)) - return c.NewNetwork(driver, name, nwOptions...) + n, err := c.NewNetwork(driver, name, nwOptions...) + if err != nil { + return nil, err + } + + daemon.LogNetworkEvent(n, "create") + return n, nil } func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) { @@ -178,3 +186,21 @@ func (daemon *Daemon) GetNetworkDriverList() map[string]bool { return pluginList } + +// DeleteNetwork destroys a network unless it's one of docker's predefined networks. +func (daemon *Daemon) DeleteNetwork(networkID string) error { + nw, err := daemon.FindNetwork(networkID) + if err != nil { + return err + } + + if runconfig.IsPreDefinedNetwork(nw.Name()) { + return derr.ErrorCodeCantDeletePredefinedNetwork.WithArgs(nw.Name()) + } + + if err := nw.Delete(); err != nil { + return err + } + daemon.LogNetworkEvent(nw, "destroy") + return nil +} diff --git a/errors/daemon.go b/errors/daemon.go index c32ae2926b..0a1bd7d477 100644 --- a/errors/daemon.go +++ b/errors/daemon.go @@ -939,4 +939,13 @@ var ( Description: "There was an error while trying to start a container", HTTPStatusCode: http.StatusInternalServerError, }) + + // ErrorCodeCantDeletePredefinedNetwork is generated when one of the predefined networks + // is attempted to be deleted. + ErrorCodeCantDeletePredefinedNetwork = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CANT_DELETE_PREDEFINED_NETWORK", + Message: "%s is a pre-defined network and cannot be removed", + Description: "Engine's predefined networks cannot be deleted", + HTTPStatusCode: http.StatusForbidden, + }) ) diff --git a/integration-cli/docker_cli_events_unix_test.go b/integration-cli/docker_cli_events_unix_test.go index a6353d0453..4144b6bfce 100644 --- a/integration-cli/docker_cli_events_unix_test.go +++ b/integration-cli/docker_cli_events_unix_test.go @@ -177,3 +177,29 @@ func (s *DockerSuite) TestVolumeEvents(c *check.C) { c.Assert(volumeEvents[2], checker.Equals, "unmount") c.Assert(volumeEvents[3], checker.Equals, "destroy") } + +func (s *DockerSuite) TestNetworkEvents(c *check.C) { + testRequires(c, DaemonIsLinux) + + since := daemonTime(c).Unix() + + // Observe create/connect network actions + dockerCmd(c, "network", "create", "test-event-network-local") + dockerCmd(c, "run", "--name", "test-network-container", "--net", "test-event-network-local", "-d", "busybox", "true") + waitRun("test-network-container") + + // Observe disconnect/destroy network actions + dockerCmd(c, "rm", "-f", "test-network-container") + dockerCmd(c, "network", "rm", "test-event-network-local") + + out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix())) + events := strings.Split(strings.TrimSpace(out), "\n") + c.Assert(len(events), checker.GreaterThan, 4) + + netEvents := eventActionsByIDAndType(c, events, "test-event-network-local", "network") + c.Assert(netEvents, checker.HasLen, 4) + c.Assert(netEvents[0], checker.Equals, "create") + c.Assert(netEvents[1], checker.Equals, "connect") + c.Assert(netEvents[2], checker.Equals, "disconnect") + c.Assert(netEvents[3], checker.Equals, "destroy") +}