diff --git a/README.md b/README.md index 0cec2abc..85b5c7cd 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ [![Join the chat at https://gitter.im/pumba-chaos/Lobby](https://badges.gitter.im/pumba-chaos/Lobby.svg)](https://gitter.im/pumba-chaos/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Falexei-led%2Fpumba%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/alexei-led/pumba/goto?ref=master) +[![](https://github.com/alexei-led/pumba/workflows/Pumba%20CI/badge.svg)](https://github.com/alexei-led/pumba/actions?query=workflow%3A"Pumba+CI") [![Go Report Card](https://goreportcard.com/badge/github.com/alexei-led/pumba)](https://goreportcard.com/report/github.com/alexei-led/pumba) [![codecov](https://codecov.io/gh/alexei-led/pumba/branch/master/graph/badge.svg)](https://codecov.io/gh/alexei-led/pumba) -[![](https://badge.imagelayers.io/gaiaadm/pumba:master.svg)](https://imagelayers.io/?images=gaiaadm/pumba:master) [![](https://images.microbadger.com/badges/image/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) [![](https://images.microbadger.com/badges/version/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) [![](https://images.microbadger.com/badges/commit/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) + +[![](https://images.microbadger.com/badges/image/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) [![](https://images.microbadger.com/badges/version/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) [![](https://images.microbadger.com/badges/commit/gaiaadm/pumba.svg)](http://microbadger.com/images/gaiaadm/pumba) ## Logo @@ -51,6 +52,7 @@ GLOBAL OPTIONS: --slackhook value web hook url; send Pumba log events to Slack --slackchannel value Slack channel (default #pumba) (default: "#pumba") --interval value, -i value recurrent interval for chaos command; use with optional unit suffix: 'ms/s/m/h' + --label value filter containers by labels, e.g '--label key=value' (multiple labels supported) --random, -r randomly select single matching container from list of target containers --dry dry runl does not create chaos, only logs planned chaos commands --help, -h show help @@ -66,13 +68,14 @@ NAME: pumba kill - kill specified containers USAGE: - pumba kill [command options] containers (name, list of names, RE2 regex) + pumba [global options] kill [command options] containers (name, list of names, RE2 regex) DESCRIPTION: send termination signal to the main process inside target container(s) OPTIONS: --signal value, -s value termination signal, that will be sent by Pumba to the main process inside target container(s) (default: "SIGKILL") + --limit value, -l value limit to number of container to kill (0: kill all matching) (default: 0) ``` ### Pause Container command diff --git a/VERSION b/VERSION index ef5e4454..2228cad4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.5 +0.6.7 diff --git a/cmd/main.go b/cmd/main.go index 45b6019e..85feca66 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -147,6 +147,10 @@ func main() { Name: "interval, i", Usage: "recurrent interval for chaos command; use with optional unit suffix: 'ms/s/m/h'", }, + cli.StringSliceFlag{ + Name: "label", + Usage: "filter containers by labels, e.g '--label key=value' (multiple labels supported)", + }, cli.BoolFlag{ Name: "random, r", Usage: "randomly select single matching container from list of target containers", diff --git a/go.mod b/go.mod index f4f584bd..ab563640 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/alexei-led/pumba require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.11 // indirect - github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/containerd/containerd v1.3.2 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 @@ -13,18 +13,21 @@ require ( github.com/gorilla/mux v1.7.0 // indirect github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 // indirect github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07 + github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/pkg/errors v0.8.1 // indirect github.com/sirupsen/logrus v1.3.0 github.com/stretchr/testify v1.3.0 github.com/urfave/cli v1.20.0 - golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 // indirect - golang.org/x/net v0.0.0-20190213061140-3a22650c66bd - golang.org/x/sys v0.0.0-20190214214411-e77772198cdc // indirect + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect google.golang.org/grpc v1.18.0 // indirect gotest.tools v2.2.0+incompatible // indirect ) -replace github.com/docker/docker v1.13.1 => github.com/docker/engine v0.0.0-20190408150954-50ebe4562dfc +replace github.com/docker/docker v1.13.1 => github.com/docker/engine v1.4.2-0.20191113042239-ea84732a7725 + +go 1.13 diff --git a/go.sum b/go.sum index 3d350b98..3e5a5dd2 100644 --- a/go.sum +++ b/go.sum @@ -3,16 +3,16 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7O github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/engine v0.0.0-20190408150954-50ebe4562dfc h1:Hr7acZ8sorvBVY3kxRZT6FB6W6hGfrKjqWzhAWLXm+o= -github.com/docker/engine v0.0.0-20190408150954-50ebe4562dfc/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20191113042239-ea84732a7725 h1:aEPcmLOLK4xi6fKbzrWyiAAM+SN9sN1Pi6WrfLnPDog= +github.com/docker/engine v1.4.2-0.20191113042239-ea84732a7725/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= @@ -35,6 +35,8 @@ github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07/go.mod h1:j1kV/ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= @@ -56,24 +58,31 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190214214411-e77772198cdc h1:PkRkk0lptxM8Ms6WIrd4MztFbP96xOFP8GTRMhXsJdM= -golang.org/x/sys v0.0.0-20190214214411-e77772198cdc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= diff --git a/hack/mockgen.Dockerfile b/hack/mockgen.Dockerfile index d333317e..4670231b 100644 --- a/hack/mockgen.Dockerfile +++ b/hack/mockgen.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.10-alpine +FROM golang:1.12-alpine RUN apk add --no-cache git diff --git a/hack/test.sh b/hack/test.sh index 1f123944..b6fc64c6 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -33,7 +33,7 @@ export CGO_ENABLED=${cgo_flag} export GO111MODULE=on generate_cover_data() { - [ -d "${COVER}" ] && rm -rf "${COVER:?}/*" + [ -d "${COVER}" ] && rm -rf "${COVER:?}" [ -d "${COVER}" ] || mkdir -p "${COVER}" # Save current IFS diff --git a/mocks/APIClient.go b/mocks/APIClient.go index 1af641ee..1a26ae46 100644 --- a/mocks/APIClient.go +++ b/mocks/APIClient.go @@ -2,21 +2,37 @@ package mocks -import container "github.com/docker/docker/api/types/container" -import context "context" -import events "github.com/docker/docker/api/types/events" -import filters "github.com/docker/docker/api/types/filters" -import http "net/http" -import image "github.com/docker/docker/api/types/image" -import io "io" -import mock "github.com/stretchr/testify/mock" -import net "net" -import network "github.com/docker/docker/api/types/network" -import registry "github.com/docker/docker/api/types/registry" -import swarm "github.com/docker/docker/api/types/swarm" -import time "time" -import types "github.com/docker/docker/api/types" -import volume "github.com/docker/docker/api/types/volume" +import ( + context "context" + + container "github.com/docker/docker/api/types/container" + + events "github.com/docker/docker/api/types/events" + + filters "github.com/docker/docker/api/types/filters" + + http "net/http" + + image "github.com/docker/docker/api/types/image" + + io "io" + + mock "github.com/stretchr/testify/mock" + + net "net" + + network "github.com/docker/docker/api/types/network" + + registry "github.com/docker/docker/api/types/registry" + + swarm "github.com/docker/docker/api/types/swarm" + + time "time" + + types "github.com/docker/docker/api/types" + + volume "github.com/docker/docker/api/types/volume" +) // APIClient is an autogenerated mock type for the APIClient type type APIClient struct { @@ -852,13 +868,13 @@ func (_m *APIClient) DaemonHost() string { return r0 } -// DialSession provides a mock function with given fields: ctx, proto, meta -func (_m *APIClient) DialSession(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { - ret := _m.Called(ctx, proto, meta) +// DialHijack provides a mock function with given fields: ctx, url, proto, meta +func (_m *APIClient) DialHijack(ctx context.Context, url string, proto string, meta map[string][]string) (net.Conn, error) { + ret := _m.Called(ctx, url, proto, meta) var r0 net.Conn - if rf, ok := ret.Get(0).(func(context.Context, string, map[string][]string) net.Conn); ok { - r0 = rf(ctx, proto, meta) + if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string][]string) net.Conn); ok { + r0 = rf(ctx, url, proto, meta) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(net.Conn) @@ -866,8 +882,8 @@ func (_m *APIClient) DialSession(ctx context.Context, proto string, meta map[str } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, map[string][]string) error); ok { - r1 = rf(ctx, proto, meta) + if rf, ok := ret.Get(1).(func(context.Context, string, string, map[string][]string) error); ok { + r1 = rf(ctx, url, proto, meta) } else { r1 = ret.Error(1) } diff --git a/mocks/CheckpointAPIClient.go b/mocks/CheckpointAPIClient.go index 392dd867..c7d47774 100644 --- a/mocks/CheckpointAPIClient.go +++ b/mocks/CheckpointAPIClient.go @@ -2,9 +2,12 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + types "github.com/docker/docker/api/types" + mock "github.com/stretchr/testify/mock" +) // CheckpointAPIClient is an autogenerated mock type for the CheckpointAPIClient type type CheckpointAPIClient struct { diff --git a/mocks/CommonAPIClient.go b/mocks/CommonAPIClient.go index 77c3c327..9d33d442 100644 --- a/mocks/CommonAPIClient.go +++ b/mocks/CommonAPIClient.go @@ -2,21 +2,37 @@ package mocks -import container "github.com/docker/docker/api/types/container" -import context "context" -import events "github.com/docker/docker/api/types/events" -import filters "github.com/docker/docker/api/types/filters" -import http "net/http" -import image "github.com/docker/docker/api/types/image" -import io "io" -import mock "github.com/stretchr/testify/mock" -import net "net" -import network "github.com/docker/docker/api/types/network" -import registry "github.com/docker/docker/api/types/registry" -import swarm "github.com/docker/docker/api/types/swarm" -import time "time" -import types "github.com/docker/docker/api/types" -import volume "github.com/docker/docker/api/types/volume" +import ( + context "context" + + container "github.com/docker/docker/api/types/container" + + events "github.com/docker/docker/api/types/events" + + filters "github.com/docker/docker/api/types/filters" + + http "net/http" + + image "github.com/docker/docker/api/types/image" + + io "io" + + mock "github.com/stretchr/testify/mock" + + net "net" + + network "github.com/docker/docker/api/types/network" + + registry "github.com/docker/docker/api/types/registry" + + swarm "github.com/docker/docker/api/types/swarm" + + time "time" + + types "github.com/docker/docker/api/types" + + volume "github.com/docker/docker/api/types/volume" +) // CommonAPIClient is an autogenerated mock type for the CommonAPIClient type type CommonAPIClient struct { @@ -801,13 +817,13 @@ func (_m *CommonAPIClient) DaemonHost() string { return r0 } -// DialSession provides a mock function with given fields: ctx, proto, meta -func (_m *CommonAPIClient) DialSession(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { - ret := _m.Called(ctx, proto, meta) +// DialHijack provides a mock function with given fields: ctx, url, proto, meta +func (_m *CommonAPIClient) DialHijack(ctx context.Context, url string, proto string, meta map[string][]string) (net.Conn, error) { + ret := _m.Called(ctx, url, proto, meta) var r0 net.Conn - if rf, ok := ret.Get(0).(func(context.Context, string, map[string][]string) net.Conn); ok { - r0 = rf(ctx, proto, meta) + if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string][]string) net.Conn); ok { + r0 = rf(ctx, url, proto, meta) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(net.Conn) @@ -815,8 +831,8 @@ func (_m *CommonAPIClient) DialSession(ctx context.Context, proto string, meta m } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, map[string][]string) error); ok { - r1 = rf(ctx, proto, meta) + if rf, ok := ret.Get(1).(func(context.Context, string, string, map[string][]string) error); ok { + r1 = rf(ctx, url, proto, meta) } else { r1 = ret.Error(1) } diff --git a/mocks/ConfigAPIClient.go b/mocks/ConfigAPIClient.go index 19fc1b60..5ab9445a 100644 --- a/mocks/ConfigAPIClient.go +++ b/mocks/ConfigAPIClient.go @@ -2,10 +2,14 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import swarm "github.com/docker/docker/api/types/swarm" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + swarm "github.com/docker/docker/api/types/swarm" + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" +) // ConfigAPIClient is an autogenerated mock type for the ConfigAPIClient type type ConfigAPIClient struct { diff --git a/mocks/ContainerAPIClient.go b/mocks/ContainerAPIClient.go index f2594c34..1626f563 100644 --- a/mocks/ContainerAPIClient.go +++ b/mocks/ContainerAPIClient.go @@ -2,14 +2,23 @@ package mocks -import container "github.com/docker/docker/api/types/container" -import context "context" -import filters "github.com/docker/docker/api/types/filters" -import io "io" -import mock "github.com/stretchr/testify/mock" -import network "github.com/docker/docker/api/types/network" -import time "time" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + container "github.com/docker/docker/api/types/container" + + filters "github.com/docker/docker/api/types/filters" + + io "io" + + mock "github.com/stretchr/testify/mock" + + network "github.com/docker/docker/api/types/network" + + time "time" + + types "github.com/docker/docker/api/types" +) // ContainerAPIClient is an autogenerated mock type for the ContainerAPIClient type type ContainerAPIClient struct { diff --git a/mocks/DistributionAPIClient.go b/mocks/DistributionAPIClient.go index 26b24af0..232bda11 100644 --- a/mocks/DistributionAPIClient.go +++ b/mocks/DistributionAPIClient.go @@ -2,9 +2,12 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import registry "github.com/docker/docker/api/types/registry" +import ( + context "context" + + registry "github.com/docker/docker/api/types/registry" + mock "github.com/stretchr/testify/mock" +) // DistributionAPIClient is an autogenerated mock type for the DistributionAPIClient type type DistributionAPIClient struct { diff --git a/mocks/ImageAPIClient.go b/mocks/ImageAPIClient.go index f8445eda..b9d2e1f9 100644 --- a/mocks/ImageAPIClient.go +++ b/mocks/ImageAPIClient.go @@ -2,13 +2,20 @@ package mocks -import context "context" -import filters "github.com/docker/docker/api/types/filters" -import image "github.com/docker/docker/api/types/image" -import io "io" -import mock "github.com/stretchr/testify/mock" -import registry "github.com/docker/docker/api/types/registry" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + filters "github.com/docker/docker/api/types/filters" + image "github.com/docker/docker/api/types/image" + + io "io" + + mock "github.com/stretchr/testify/mock" + + registry "github.com/docker/docker/api/types/registry" + + types "github.com/docker/docker/api/types" +) // ImageAPIClient is an autogenerated mock type for the ImageAPIClient type type ImageAPIClient struct { diff --git a/mocks/NetworkAPIClient.go b/mocks/NetworkAPIClient.go index 166bd929..90d69410 100644 --- a/mocks/NetworkAPIClient.go +++ b/mocks/NetworkAPIClient.go @@ -2,11 +2,16 @@ package mocks -import context "context" -import filters "github.com/docker/docker/api/types/filters" -import mock "github.com/stretchr/testify/mock" -import network "github.com/docker/docker/api/types/network" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + filters "github.com/docker/docker/api/types/filters" + mock "github.com/stretchr/testify/mock" + + network "github.com/docker/docker/api/types/network" + + types "github.com/docker/docker/api/types" +) // NetworkAPIClient is an autogenerated mock type for the NetworkAPIClient type type NetworkAPIClient struct { diff --git a/mocks/NodeAPIClient.go b/mocks/NodeAPIClient.go index f8d1d568..7d4b6438 100644 --- a/mocks/NodeAPIClient.go +++ b/mocks/NodeAPIClient.go @@ -2,10 +2,14 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import swarm "github.com/docker/docker/api/types/swarm" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + swarm "github.com/docker/docker/api/types/swarm" + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" +) // NodeAPIClient is an autogenerated mock type for the NodeAPIClient type type NodeAPIClient struct { diff --git a/mocks/PluginAPIClient.go b/mocks/PluginAPIClient.go index 1d13fbf2..68621273 100644 --- a/mocks/PluginAPIClient.go +++ b/mocks/PluginAPIClient.go @@ -2,11 +2,16 @@ package mocks -import context "context" -import filters "github.com/docker/docker/api/types/filters" -import io "io" -import mock "github.com/stretchr/testify/mock" -import types "github.com/docker/docker/api/types" +import ( + context "context" + io "io" + + filters "github.com/docker/docker/api/types/filters" + + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" +) // PluginAPIClient is an autogenerated mock type for the PluginAPIClient type type PluginAPIClient struct { diff --git a/mocks/SecretAPIClient.go b/mocks/SecretAPIClient.go index 06b17b55..c0ffc7e2 100644 --- a/mocks/SecretAPIClient.go +++ b/mocks/SecretAPIClient.go @@ -2,10 +2,14 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import swarm "github.com/docker/docker/api/types/swarm" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + swarm "github.com/docker/docker/api/types/swarm" + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" +) // SecretAPIClient is an autogenerated mock type for the SecretAPIClient type type SecretAPIClient struct { diff --git a/mocks/ServiceAPIClient.go b/mocks/ServiceAPIClient.go index a7362a9e..7a1a07b2 100644 --- a/mocks/ServiceAPIClient.go +++ b/mocks/ServiceAPIClient.go @@ -2,11 +2,16 @@ package mocks -import context "context" -import io "io" -import mock "github.com/stretchr/testify/mock" -import swarm "github.com/docker/docker/api/types/swarm" -import types "github.com/docker/docker/api/types" +import ( + context "context" + io "io" + + mock "github.com/stretchr/testify/mock" + + swarm "github.com/docker/docker/api/types/swarm" + + types "github.com/docker/docker/api/types" +) // ServiceAPIClient is an autogenerated mock type for the ServiceAPIClient type type ServiceAPIClient struct { diff --git a/mocks/SwarmAPIClient.go b/mocks/SwarmAPIClient.go index 5ca32414..90975e20 100644 --- a/mocks/SwarmAPIClient.go +++ b/mocks/SwarmAPIClient.go @@ -2,10 +2,14 @@ package mocks -import context "context" -import mock "github.com/stretchr/testify/mock" -import swarm "github.com/docker/docker/api/types/swarm" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + swarm "github.com/docker/docker/api/types/swarm" + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" +) // SwarmAPIClient is an autogenerated mock type for the SwarmAPIClient type type SwarmAPIClient struct { diff --git a/mocks/SystemAPIClient.go b/mocks/SystemAPIClient.go index 288d91cd..fcb45e5a 100644 --- a/mocks/SystemAPIClient.go +++ b/mocks/SystemAPIClient.go @@ -2,11 +2,16 @@ package mocks -import context "context" -import events "github.com/docker/docker/api/types/events" -import mock "github.com/stretchr/testify/mock" -import registry "github.com/docker/docker/api/types/registry" -import types "github.com/docker/docker/api/types" +import ( + context "context" + + events "github.com/docker/docker/api/types/events" + mock "github.com/stretchr/testify/mock" + + registry "github.com/docker/docker/api/types/registry" + + types "github.com/docker/docker/api/types" +) // SystemAPIClient is an autogenerated mock type for the SystemAPIClient type type SystemAPIClient struct { diff --git a/mocks/VolumeAPIClient.go b/mocks/VolumeAPIClient.go index 9b15ca83..1fcef300 100644 --- a/mocks/VolumeAPIClient.go +++ b/mocks/VolumeAPIClient.go @@ -2,11 +2,16 @@ package mocks -import context "context" -import filters "github.com/docker/docker/api/types/filters" -import mock "github.com/stretchr/testify/mock" -import types "github.com/docker/docker/api/types" -import volume "github.com/docker/docker/api/types/volume" +import ( + context "context" + + filters "github.com/docker/docker/api/types/filters" + mock "github.com/stretchr/testify/mock" + + types "github.com/docker/docker/api/types" + + volume "github.com/docker/docker/api/types/volume" +) // VolumeAPIClient is an autogenerated mock type for the VolumeAPIClient type type VolumeAPIClient struct { diff --git a/pkg/chaos/docker/cmd/kill.go b/pkg/chaos/docker/cmd/kill.go index 9c330ed0..5eae36c0 100644 --- a/pkg/chaos/docker/cmd/kill.go +++ b/pkg/chaos/docker/cmd/kill.go @@ -32,7 +32,7 @@ func NewKillCLICommand(ctx context.Context) *cli.Command { }, }, Usage: "kill specified containers", - ArgsUsage: fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q", chaos.Re2Prefix), + ArgsUsage: fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q)", chaos.Re2Prefix), Description: "send termination signal to the main process inside target container(s)", Action: cmdContext.kill, } @@ -42,6 +42,8 @@ func NewKillCLICommand(ctx context.Context) *cli.Command { func (cmd *killContext) kill(c *cli.Context) error { // get random random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get interval @@ -53,7 +55,7 @@ func (cmd *killContext) kill(c *cli.Context) error { // get limit for number of containers to kill limit := c.Int("limit") // init kill command - killCommand, err := docker.NewKillCommand(chaos.DockerClient, names, pattern, signal, limit, dryRun) + killCommand, err := docker.NewKillCommand(chaos.DockerClient, names, pattern, labels, signal, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/docker/cmd/pause.go b/pkg/chaos/docker/cmd/pause.go index fd198e56..f3153d3d 100644 --- a/pkg/chaos/docker/cmd/pause.go +++ b/pkg/chaos/docker/cmd/pause.go @@ -43,6 +43,8 @@ func (cmd *pauseContext) pause(c *cli.Context) error { random := c.GlobalBool("random") // get dry-run mode dryRun := c.GlobalBool("dry-run") + // get labels + labels := c.GlobalStringSlice("label") // get global chaos interval interval := c.GlobalString("interval") // get limit for number of containers to kill @@ -52,7 +54,7 @@ func (cmd *pauseContext) pause(c *cli.Context) error { // get chaos command duration duration := c.String("duration") // init pause command - pauseCommand, err := docker.NewPauseCommand(chaos.DockerClient, names, pattern, interval, duration, limit, dryRun) + pauseCommand, err := docker.NewPauseCommand(chaos.DockerClient, names, pattern, labels, interval, duration, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/docker/cmd/remove.go b/pkg/chaos/docker/cmd/remove.go index 100cf598..df43a143 100644 --- a/pkg/chaos/docker/cmd/remove.go +++ b/pkg/chaos/docker/cmd/remove.go @@ -49,6 +49,8 @@ func NewRemoveCLICommand(ctx context.Context) *cli.Command { func (cmd *removeContext) remove(c *cli.Context) error { // get random random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get interval @@ -64,7 +66,7 @@ func (cmd *removeContext) remove(c *cli.Context) error { // get limit for number of containers to remove limit := c.Int("limit") // init remove command - removeCommand, err := docker.NewRemoveCommand(chaos.DockerClient, names, pattern, force, links, volumes, limit, dryRun) + removeCommand, err := docker.NewRemoveCommand(chaos.DockerClient, names, pattern, labels, force, links, volumes, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/docker/cmd/stop.go b/pkg/chaos/docker/cmd/stop.go index 4a4eae2a..931df1fd 100644 --- a/pkg/chaos/docker/cmd/stop.go +++ b/pkg/chaos/docker/cmd/stop.go @@ -51,6 +51,8 @@ func NewStopCLICommand(ctx context.Context) *cli.Command { func (cmd *stopContext) stop(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get global chaos interval @@ -66,7 +68,7 @@ func (cmd *stopContext) stop(c *cli.Context) error { // get chaos command duration duration := c.String("duration") // init stop command - stopCommand, err := docker.NewStopCommand(chaos.DockerClient, names, pattern, restart, interval, duration, waitTime, limit, dryRun) + stopCommand, err := docker.NewStopCommand(chaos.DockerClient, names, pattern, labels, restart, interval, duration, waitTime, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/docker/kill.go b/pkg/chaos/docker/kill.go index cdf80150..e8e7c3bf 100644 --- a/pkg/chaos/docker/kill.go +++ b/pkg/chaos/docker/kill.go @@ -54,14 +54,15 @@ type KillCommand struct { client container.Client names []string pattern string + labels []string signal string limit int dryRun bool } // NewKillCommand create new Kill Command instance -func NewKillCommand(client container.Client, names []string, pattern string, signal string, limit int, dryRun bool) (chaos.Command, error) { - kill := &KillCommand{client, names, pattern, signal, limit, dryRun} +func NewKillCommand(client container.Client, names []string, pattern string, labels []string, signal string, limit int, dryRun bool) (chaos.Command, error) { + kill := &KillCommand{client, names, pattern, labels, signal, limit, dryRun} if kill.signal == "" { kill.signal = DefaultKillSignal } @@ -79,9 +80,10 @@ func (k *KillCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": k.names, "pattern": k.pattern, + "labels": k.labels, "limit": k.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, k.client, k.names, k.pattern, k.limit) + containers, err := container.ListNContainers(ctx, k.client, k.names, k.pattern, k.labels, k.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/docker/kill_test.go b/pkg/chaos/docker/kill_test.go index 83a86950..c7543d60 100644 --- a/pkg/chaos/docker/kill_test.go +++ b/pkg/chaos/docker/kill_test.go @@ -19,6 +19,7 @@ func TestKillCommand_Run(t *testing.T) { type fields struct { names []string pattern string + labels []string signal string limit int dryRun bool @@ -46,6 +47,18 @@ func TestKillCommand_Run(t *testing.T) { }, expected: container.CreateTestContainers(3), }, + { + name: "kill matching labeled containers by names", + fields: fields{ + names: []string{"c1", "c2", "c3"}, + labels: []string{"key=value"}, + signal: "SIGKILL", + }, + args: args{ + ctx: context.TODO(), + }, + expected: container.CreateLabeledTestContainers(3, map[string]string{"key": "value"}), + }, { name: "kill matching containers by filter with limit", fields: fields{ @@ -113,11 +126,13 @@ func TestKillCommand_Run(t *testing.T) { client: mockClient, names: tt.fields.names, pattern: tt.fields.pattern, + labels: tt.fields.labels, signal: tt.fields.signal, limit: tt.fields.limit, dryRun: tt.fields.dryRun, } - call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.Filter")) + opts := container.ListOpts{Labels: tt.fields.labels} + call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.FilterFunc"), opts) if tt.errs.listError { call.Return(tt.expected, errors.New("ERROR")) goto Invoke @@ -156,6 +171,7 @@ func TestNewKillCommand(t *testing.T) { client container.Client names []string pattern string + labels []string signal string limit int dryRun bool @@ -201,7 +217,7 @@ func TestNewKillCommand(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewKillCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.signal, tt.args.limit, tt.args.dryRun) + got, err := NewKillCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.labels, tt.args.signal, tt.args.limit, tt.args.dryRun) if (err != nil) != tt.wantErr { t.Errorf("NewKillCommand() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/chaos/docker/pause.go b/pkg/chaos/docker/pause.go index d92b77b3..ebaa79f5 100644 --- a/pkg/chaos/docker/pause.go +++ b/pkg/chaos/docker/pause.go @@ -15,13 +15,14 @@ type PauseCommand struct { client container.Client names []string pattern string + labels []string duration time.Duration limit int dryRun bool } // NewPauseCommand create new Pause Command instance -func NewPauseCommand(client container.Client, names []string, pattern string, intervalStr string, durationStr string, limit int, dryRun bool) (chaos.Command, error) { +func NewPauseCommand(client container.Client, names []string, pattern string, labels []string, intervalStr string, durationStr string, limit int, dryRun bool) (chaos.Command, error) { // get interval interval, err := util.GetIntervalValue(intervalStr) if err != nil { @@ -32,7 +33,7 @@ func NewPauseCommand(client container.Client, names []string, pattern string, in if err != nil { return nil, err } - return &PauseCommand{client, names, pattern, duration, limit, dryRun}, nil + return &PauseCommand{client, names, pattern, labels, duration, limit, dryRun}, nil } // Run pause command @@ -41,10 +42,11 @@ func (p *PauseCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": p.names, "pattern": p.pattern, + "labels": p.labels, "duration": p.duration, "limit": p.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, p.client, p.names, p.pattern, p.limit) + containers, err := container.ListNContainers(ctx, p.client, p.names, p.pattern, p.labels, p.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/docker/pause_test.go b/pkg/chaos/docker/pause_test.go index c873e45e..21c57246 100644 --- a/pkg/chaos/docker/pause_test.go +++ b/pkg/chaos/docker/pause_test.go @@ -17,6 +17,7 @@ func TestNewPauseCommand(t *testing.T) { client container.Client names []string pattern string + labels []string interval string duration string limit int @@ -47,7 +48,7 @@ func TestNewPauseCommand(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewPauseCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.interval, tt.args.duration, tt.args.limit, tt.args.dryRun) + got, err := NewPauseCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.labels, tt.args.interval, tt.args.duration, tt.args.limit, tt.args.dryRun) if (err != nil) != tt.wantErr { t.Errorf("NewPauseCommand() error = %v, wantErr %v", err, tt.wantErr) return @@ -170,7 +171,7 @@ func TestPauseCommand_Run(t *testing.T) { limit: tt.fields.limit, dryRun: tt.fields.dryRun, } - call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.Filter")) + call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.FilterFunc"), mock.AnythingOfType("container.ListOpts")) if tt.errs.listError { call.Return(tt.expected, errors.New("ERROR")) goto Invoke diff --git a/pkg/chaos/docker/remove.go b/pkg/chaos/docker/remove.go index 758b5841..ded43a48 100644 --- a/pkg/chaos/docker/remove.go +++ b/pkg/chaos/docker/remove.go @@ -13,6 +13,7 @@ type RemoveCommand struct { client container.Client names []string pattern string + labels []string force bool links bool volumes bool @@ -21,8 +22,8 @@ type RemoveCommand struct { } // NewRemoveCommand create new Kill Command instance -func NewRemoveCommand(client container.Client, names []string, pattern string, force bool, links bool, volumes bool, limit int, dryRun bool) (chaos.Command, error) { - remove := &RemoveCommand{client, names, pattern, force, links, volumes, limit, dryRun} +func NewRemoveCommand(client container.Client, names []string, pattern string, labels []string, force bool, links bool, volumes bool, limit int, dryRun bool) (chaos.Command, error) { + remove := &RemoveCommand{client, names, pattern, labels, force, links, volumes, limit, dryRun} return remove, nil } @@ -32,9 +33,10 @@ func (r *RemoveCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": r.names, "pattern": r.pattern, + "labels": r.labels, "limit": r.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, r.client, r.names, r.pattern, r.limit) + containers, err := container.ListNContainers(ctx, r.client, r.names, r.pattern, r.labels, r.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/docker/remove_test.go b/pkg/chaos/docker/remove_test.go index 12a32ff2..5cf9fff8 100644 --- a/pkg/chaos/docker/remove_test.go +++ b/pkg/chaos/docker/remove_test.go @@ -121,7 +121,7 @@ func TestRemoveCommand_Run(t *testing.T) { limit: tt.fields.limit, dryRun: tt.fields.dryRun, } - call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.Filter")) + call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.FilterFunc"), mock.AnythingOfType("container.ListOpts")) if tt.errs.listError { call.Return(tt.expected, errors.New("ERROR")) goto Invoke @@ -160,6 +160,7 @@ func TestNewRemoveCommand(t *testing.T) { client container.Client names []string pattern string + labels []string force bool links bool volumes bool @@ -192,7 +193,7 @@ func TestNewRemoveCommand(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewRemoveCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.force, tt.args.links, tt.args.volumes, tt.args.limit, tt.args.dryRun) + got, err := NewRemoveCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.labels, tt.args.force, tt.args.links, tt.args.volumes, tt.args.limit, tt.args.dryRun) if (err != nil) != tt.wantErr { t.Errorf("NewRemoveCommand() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/chaos/docker/stop.go b/pkg/chaos/docker/stop.go index d1c853c4..a2a4b7ef 100644 --- a/pkg/chaos/docker/stop.go +++ b/pkg/chaos/docker/stop.go @@ -20,6 +20,7 @@ type StopCommand struct { client container.Client names []string pattern string + labels []string restart bool duration time.Duration waitTime int @@ -28,7 +29,7 @@ type StopCommand struct { } // NewStopCommand create new Stop Command instance -func NewStopCommand(client container.Client, names []string, pattern string, restart bool, intervalStr string, durationStr string, waitTime int, limit int, dryRun bool) (chaos.Command, error) { +func NewStopCommand(client container.Client, names []string, pattern string, labels []string, restart bool, intervalStr string, durationStr string, waitTime int, limit int, dryRun bool) (chaos.Command, error) { if waitTime <= 0 { waitTime = DeafultWaitTime } @@ -42,7 +43,7 @@ func NewStopCommand(client container.Client, names []string, pattern string, res if err != nil { return nil, err } - return &StopCommand{client, names, pattern, restart, duration, waitTime, limit, dryRun}, nil + return &StopCommand{client, names, pattern, labels, restart, duration, waitTime, limit, dryRun}, nil } // Run stop command @@ -51,11 +52,12 @@ func (s *StopCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": s.names, "pattern": s.pattern, + "labels": s.labels, "duration": s.duration, "waitTime": s.waitTime, "limit": s.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, s.client, s.names, s.pattern, s.limit) + containers, err := container.ListNContainers(ctx, s.client, s.names, s.pattern, s.labels, s.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/docker/stop_test.go b/pkg/chaos/docker/stop_test.go index 220261a4..c0d62a55 100644 --- a/pkg/chaos/docker/stop_test.go +++ b/pkg/chaos/docker/stop_test.go @@ -17,6 +17,7 @@ func TestNewStopCommand(t *testing.T) { client container.Client names []string pattern string + labels []string restart bool interval string duration string @@ -70,7 +71,7 @@ func TestNewStopCommand(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewStopCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.restart, tt.args.interval, tt.args.duration, tt.args.waitTime, tt.args.limit, tt.args.dryRun) + got, err := NewStopCommand(tt.args.client, tt.args.names, tt.args.pattern, tt.args.labels, tt.args.restart, tt.args.interval, tt.args.duration, tt.args.waitTime, tt.args.limit, tt.args.dryRun) if (err != nil) != tt.wantErr { t.Errorf("NewStopCommand() error = %v, wantErr %v", err, tt.wantErr) return @@ -230,7 +231,7 @@ func TestStopCommand_Run(t *testing.T) { limit: tt.fields.limit, dryRun: tt.fields.dryRun, } - call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.Filter")) + call := mockClient.On("ListContainers", tt.args.ctx, mock.AnythingOfType("container.FilterFunc"), mock.AnythingOfType("container.ListOpts")) if tt.errs.listError { call.Return(tt.expected, errors.New("ERROR")) goto Invoke diff --git a/pkg/chaos/netem/cmd/corrupt.go b/pkg/chaos/netem/cmd/corrupt.go index f734f5b7..1f3b323e 100644 --- a/pkg/chaos/netem/cmd/corrupt.go +++ b/pkg/chaos/netem/cmd/corrupt.go @@ -42,6 +42,8 @@ func NewCorruptCLICommand(ctx context.Context) *cli.Command { func (cmd *corruptContext) corrupt(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -68,7 +70,7 @@ func (cmd *corruptContext) corrupt(c *cli.Context) error { correlation := c.Float64("correlation") // init netem corrupt command - corruptCommand, err := netem.NewCorruptCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) + corruptCommand, err := netem.NewCorruptCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/delay.go b/pkg/chaos/netem/cmd/delay.go index 67856545..172ff667 100644 --- a/pkg/chaos/netem/cmd/delay.go +++ b/pkg/chaos/netem/cmd/delay.go @@ -52,6 +52,8 @@ func NewDelayCLICommand(ctx context.Context) *cli.Command { func (cmd *delayContext) delay(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -82,7 +84,7 @@ func (cmd *delayContext) delay(c *cli.Context) error { distribution := c.String("distribution") // init netem delay command - delayCommand, err := netem.NewDelayCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, time, jitter, correlation, distribution, image, pull, limit, dryRun) + delayCommand, err := netem.NewDelayCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, time, jitter, correlation, distribution, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/duplicate.go b/pkg/chaos/netem/cmd/duplicate.go index adcd6fb8..750fa30c 100644 --- a/pkg/chaos/netem/cmd/duplicate.go +++ b/pkg/chaos/netem/cmd/duplicate.go @@ -42,6 +42,8 @@ func NewDuplicateCLICommand(ctx context.Context) *cli.Command { func (cmd *duplicateContext) duplicate(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -68,7 +70,7 @@ func (cmd *duplicateContext) duplicate(c *cli.Context) error { correlation := c.Float64("correlation") // init netem duplicate command - duplicateCommand, err := netem.NewDuplicateCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) + duplicateCommand, err := netem.NewDuplicateCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/loss.go b/pkg/chaos/netem/cmd/loss.go index ca286782..65bd58c1 100644 --- a/pkg/chaos/netem/cmd/loss.go +++ b/pkg/chaos/netem/cmd/loss.go @@ -42,6 +42,8 @@ func NewLossCLICommand(ctx context.Context) *cli.Command { func (cmd *lossContext) loss(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -68,7 +70,7 @@ func (cmd *lossContext) loss(c *cli.Context) error { correlation := c.Float64("correlation") // init netem loss command - lossCommand, err := netem.NewLossCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) + lossCommand, err := netem.NewLossCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, percent, correlation, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/loss_ge.go b/pkg/chaos/netem/cmd/loss_ge.go index b83386e2..7d777872 100644 --- a/pkg/chaos/netem/cmd/loss_ge.go +++ b/pkg/chaos/netem/cmd/loss_ge.go @@ -53,6 +53,8 @@ func NewLossGECLICommand(ctx context.Context) *cli.Command { func (cmd *lossGEContext) lossGE(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -83,7 +85,7 @@ func (cmd *lossGEContext) lossGE(c *cli.Context) error { oneK := c.Float64("one-k") // init netem loss gemodel command - lossGECommand, err := netem.NewLossGECommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, pg, pb, oneH, oneK, image, pull, limit, dryRun) + lossGECommand, err := netem.NewLossGECommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, pg, pb, oneH, oneK, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/loss_state.go b/pkg/chaos/netem/cmd/loss_state.go index 5e0a1432..a272399c 100644 --- a/pkg/chaos/netem/cmd/loss_state.go +++ b/pkg/chaos/netem/cmd/loss_state.go @@ -64,6 +64,8 @@ func NewLossStateCLICommand(ctx context.Context) *cli.Command { func (cmd *lossStateContext) lossState(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -96,7 +98,7 @@ func (cmd *lossStateContext) lossState(c *cli.Context) error { p14 := c.Float64("p14") // init netem loss state command - lossStateCommand, err := netem.NewLossStateCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, p13, p31, p32, p23, p14, image, pull, limit, dryRun) + lossStateCommand, err := netem.NewLossStateCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, p13, p31, p32, p23, p14, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/cmd/rate.go b/pkg/chaos/netem/cmd/rate.go index 6f4ad124..039a8fc7 100644 --- a/pkg/chaos/netem/cmd/rate.go +++ b/pkg/chaos/netem/cmd/rate.go @@ -52,6 +52,8 @@ func NewRateCLICommand(ctx context.Context) *cli.Command { func (cmd *rateContext) rate(c *cli.Context) error { // get random flag random := c.GlobalBool("random") + // get labels + labels := c.GlobalStringSlice("label") // get dry-run mode dryRun := c.GlobalBool("dry-run") // get names or pattern @@ -82,7 +84,7 @@ func (cmd *rateContext) rate(c *cli.Context) error { cellOverhead := c.Int("celloverhead") // init netem rate command - lossCommand, err := netem.NewRateCommand(chaos.DockerClient, names, pattern, iface, ips, duration, interval, rate, packetOverhead, cellSize, cellOverhead, image, pull, limit, dryRun) + lossCommand, err := netem.NewRateCommand(chaos.DockerClient, names, pattern, labels, iface, ips, duration, interval, rate, packetOverhead, cellSize, cellOverhead, image, pull, limit, dryRun) if err != nil { return err } diff --git a/pkg/chaos/netem/corrupt.go b/pkg/chaos/netem/corrupt.go index 8237ce5f..f35574ce 100644 --- a/pkg/chaos/netem/corrupt.go +++ b/pkg/chaos/netem/corrupt.go @@ -22,6 +22,7 @@ type CorruptCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -37,6 +38,7 @@ type CorruptCommand struct { func NewCorruptCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -97,6 +99,7 @@ func NewCorruptCommand(client container.Client, return &CorruptCommand{ client: client, names: names, + labels: labels, pattern: pattern, iface: iface, ips: ips, @@ -116,9 +119,10 @@ func (n *CorruptCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/delay.go b/pkg/chaos/netem/delay.go index be2e36f9..f359c7af 100644 --- a/pkg/chaos/netem/delay.go +++ b/pkg/chaos/netem/delay.go @@ -27,6 +27,7 @@ type DelayCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -44,6 +45,7 @@ type DelayCommand struct { func NewDelayCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -117,6 +119,7 @@ func NewDelayCommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -137,9 +140,10 @@ func (n *DelayCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/delay_test.go b/pkg/chaos/netem/delay_test.go index 2f4d1058..b1b86e29 100644 --- a/pkg/chaos/netem/delay_test.go +++ b/pkg/chaos/netem/delay_test.go @@ -18,6 +18,7 @@ func TestNewDelayCommand(t *testing.T) { type args struct { names []string pattern string + labels []string iface string ipsList []string durationStr string @@ -186,7 +187,7 @@ func TestNewDelayCommand(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // invoke - got, err := NewDelayCommand(nil, tt.args.names, tt.args.pattern, tt.args.iface, tt.args.ipsList, tt.args.durationStr, tt.args.intervalStr, tt.args.time, tt.args.jitter, tt.args.correlation, tt.args.distribution, tt.args.image, tt.args.pull, tt.args.limit, tt.args.dryRun) + got, err := NewDelayCommand(nil, tt.args.names, tt.args.pattern, tt.args.labels, tt.args.iface, tt.args.ipsList, tt.args.durationStr, tt.args.intervalStr, tt.args.time, tt.args.jitter, tt.args.correlation, tt.args.distribution, tt.args.image, tt.args.pull, tt.args.limit, tt.args.dryRun) if (err != nil) != tt.wantErr { t.Errorf("NewDelayCommand() error = %v, wantErr %v", err, tt.wantErr) return @@ -342,7 +343,7 @@ func TestDelayCommand_Run(t *testing.T) { dryRun: tt.fields.dryRun, } // mock calls - call := mockClient.On("ListContainers", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("container.Filter")) + call := mockClient.On("ListContainers", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("container.FilterFunc"), mock.AnythingOfType("container.ListOpts")) if tt.errs.listError { call.Return(tt.expected, errors.New("ERROR")) goto Invoke diff --git a/pkg/chaos/netem/duplicate.go b/pkg/chaos/netem/duplicate.go index 3bd7b62e..f0013987 100644 --- a/pkg/chaos/netem/duplicate.go +++ b/pkg/chaos/netem/duplicate.go @@ -22,6 +22,7 @@ type DuplicateCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -37,6 +38,7 @@ type DuplicateCommand struct { func NewDuplicateCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -98,6 +100,7 @@ func NewDuplicateCommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -116,9 +119,10 @@ func (n *DuplicateCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/loss.go b/pkg/chaos/netem/loss.go index ad8c8b61..ef3b5f78 100644 --- a/pkg/chaos/netem/loss.go +++ b/pkg/chaos/netem/loss.go @@ -22,6 +22,7 @@ type LossCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -37,6 +38,7 @@ type LossCommand struct { func NewLossCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -98,6 +100,7 @@ func NewLossCommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -116,9 +119,10 @@ func (n *LossCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/loss_ge.go b/pkg/chaos/netem/loss_ge.go index e8b9f83a..01285e87 100644 --- a/pkg/chaos/netem/loss_ge.go +++ b/pkg/chaos/netem/loss_ge.go @@ -22,6 +22,7 @@ type LossGECommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -39,6 +40,7 @@ type LossGECommand struct { func NewLossGECommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -116,6 +118,7 @@ func NewLossGECommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -136,9 +139,10 @@ func (n *LossGECommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/loss_state.go b/pkg/chaos/netem/loss_state.go index 1f874433..8a41c6b7 100644 --- a/pkg/chaos/netem/loss_state.go +++ b/pkg/chaos/netem/loss_state.go @@ -22,6 +22,7 @@ type LossStateCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -40,6 +41,7 @@ type LossStateCommand struct { func NewLossStateCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -125,6 +127,7 @@ func NewLossStateCommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -146,9 +149,10 @@ func (n *LossStateCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/chaos/netem/rate.go b/pkg/chaos/netem/rate.go index 84563170..c519121a 100644 --- a/pkg/chaos/netem/rate.go +++ b/pkg/chaos/netem/rate.go @@ -34,6 +34,7 @@ type RateCommand struct { client container.Client names []string pattern string + labels []string iface string ips []*net.IPNet duration time.Duration @@ -51,6 +52,7 @@ type RateCommand struct { func NewRateCommand(client container.Client, names []string, // containers pattern string, // re2 regex pattern + labels []string, // filter by labels iface string, // network interface ipsList []string, // list of target ips durationStr string, // chaos duration @@ -122,6 +124,7 @@ func NewRateCommand(client container.Client, client: client, names: names, pattern: pattern, + labels: labels, iface: iface, ips: ips, duration: duration, @@ -142,9 +145,10 @@ func (n *RateCommand) Run(ctx context.Context, random bool) error { log.WithFields(log.Fields{ "names": n.names, "pattern": n.pattern, + "labels": n.labels, "limit": n.limit, }).Debug("listing matching containers") - containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.limit) + containers, err := container.ListNContainers(ctx, n.client, n.names, n.pattern, n.labels, n.limit) if err != nil { log.WithError(err).Error("failed to list containers") return err diff --git a/pkg/container/client.go b/pkg/container/client.go index 102a1173..96a9a5c5 100644 --- a/pkg/container/client.go +++ b/pkg/container/client.go @@ -15,6 +15,7 @@ import ( types "github.com/docker/docker/api/types" ctypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" dockerapi "github.com/docker/docker/client" "github.com/docker/go-connections/nat" ) @@ -25,14 +26,13 @@ const ( dryRunPrefix = "DRY: " ) -// A Filter is a prototype for a function that can be used to filter the +// A FilterFunc is a prototype for a function that can be used to filter the // results from a call to the ListContainers() method on the Client. -type Filter func(Container) bool +type FilterFunc func(Container) bool // Client interface type Client interface { - ListContainers(context.Context, Filter) ([]Container, error) - ListAllContainers(context.Context, Filter) ([]Container, error) + ListContainers(context.Context, FilterFunc, ListOpts) ([]Container, error) StopContainer(context.Context, Container, int, bool) error KillContainer(context.Context, Container, string, bool) error RemoveContainer(context.Context, Container, bool, bool, bool, bool) error @@ -75,15 +75,15 @@ type dockerClient struct { imageAPI dockerapi.ImageAPIClient } -func (client dockerClient) ListContainers(ctx context.Context, fn Filter) ([]Container, error) { - return client.listContainers(ctx, fn, types.ContainerListOptions{}) -} - -func (client dockerClient) ListAllContainers(ctx context.Context, fn Filter) ([]Container, error) { - return client.listContainers(ctx, fn, types.ContainerListOptions{All: true}) +func (client dockerClient) ListContainers(ctx context.Context, fn FilterFunc, opts ListOpts) ([]Container, error) { + filterArgs := filters.NewArgs() + for _, label := range opts.Labels { + filterArgs.Add("label", label) + } + return client.listContainers(ctx, fn, types.ContainerListOptions{All: opts.All, Filters: filterArgs}) } -func (client dockerClient) listContainers(ctx context.Context, fn Filter, opts types.ContainerListOptions) ([]Container, error) { +func (client dockerClient) listContainers(ctx context.Context, fn FilterFunc, opts types.ContainerListOptions) ([]Container, error) { log.Debug("listing containers") containers, err := client.containerAPI.ContainerList(ctx, opts) if err != nil { diff --git a/pkg/container/client_test.go b/pkg/container/client_test.go index 34bb16e6..c3fc1638 100644 --- a/pkg/container/client_test.go +++ b/pkg/container/client_test.go @@ -25,10 +25,10 @@ func NewMockEngine() *mocks.APIClient { return new(mocks.APIClient) } -func allContainers(Container) bool { +func mock_allContainers(c Container) bool { return true } -func noContainers(Container) bool { +func mock_noContainers(c Container) bool { return false } @@ -46,7 +46,7 @@ func TestListContainers_Success(t *testing.T) { api.On("ImageInspectWithRaw", mock.Anything, "abc123").Return(imageDetails, []byte{}, nil) client := dockerClient{containerAPI: api, imageAPI: api} - containers, err := client.ListContainers(context.TODO(), allContainers) + containers, err := client.ListContainers(context.TODO(), mock_allContainers, ListOpts{All: true}) assert.NoError(t, err) assert.Len(t, containers, 1) @@ -69,7 +69,7 @@ func TestListContainers_Filter(t *testing.T) { api.On("ImageInspectWithRaw", mock.Anything, "abc123").Return(imageDetails, []byte{}, nil) client := dockerClient{containerAPI: api, imageAPI: api} - containers, err := client.ListContainers(context.TODO(), noContainers) + containers, err := client.ListContainers(context.TODO(), mock_noContainers, ListOpts{}) assert.NoError(t, err) assert.Len(t, containers, 0) @@ -81,7 +81,7 @@ func TestListContainers_ListError(t *testing.T) { api.On("ContainerList", mock.Anything, mock.Anything).Return(Containers(), errors.New("oops")) client := dockerClient{containerAPI: api, imageAPI: api} - _, err := client.ListContainers(context.TODO(), allContainers) + _, err := client.ListContainers(context.TODO(), mock_allContainers, ListOpts{All: true}) assert.Error(t, err) assert.EqualError(t, err, "oops") @@ -98,7 +98,7 @@ func TestListContainers_InspectContainerError(t *testing.T) { api.On("ContainerInspect", mock.Anything, "foo").Return(ContainerDetailsResponse(AsMap()), errors.New("uh-oh")) client := dockerClient{containerAPI: api, imageAPI: api} - _, err := client.ListContainers(context.TODO(), allContainers) + _, err := client.ListContainers(context.TODO(), mock_allContainers, ListOpts{All: true}) assert.Error(t, err) assert.EqualError(t, err, "uh-oh") @@ -118,7 +118,7 @@ func TestListContainers_InspectImageError(t *testing.T) { api.On("ImageInspectWithRaw", mock.Anything, "abc123").Return(imageDetailsResponse, []byte{}, errors.New("whoops")) client := dockerClient{containerAPI: api, imageAPI: api} - _, err := client.ListContainers(context.TODO(), allContainers) + _, err := client.ListContainers(context.TODO(), mock_allContainers, ListOpts{All: true}) assert.Error(t, err) assert.EqualError(t, err, "whoops") diff --git a/pkg/container/mock_Client.go b/pkg/container/mock_Client.go index 751c7b68..33f254b9 100644 --- a/pkg/container/mock_Client.go +++ b/pkg/container/mock_Client.go @@ -2,10 +2,14 @@ package container -import context "context" -import mock "github.com/stretchr/testify/mock" -import net "net" -import time "time" +import ( + context "context" + net "net" + + mock "github.com/stretchr/testify/mock" + + time "time" +) // MockClient is an autogenerated mock type for the Client type type MockClient struct { @@ -26,36 +30,13 @@ func (_m *MockClient) KillContainer(_a0 context.Context, _a1 Container, _a2 stri return r0 } -// ListAllContainers provides a mock function with given fields: _a0, _a1 -func (_m *MockClient) ListAllContainers(_a0 context.Context, _a1 Filter) ([]Container, error) { - ret := _m.Called(_a0, _a1) - - var r0 []Container - if rf, ok := ret.Get(0).(func(context.Context, Filter) []Container); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]Container) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListContainers provides a mock function with given fields: _a0, _a1 -func (_m *MockClient) ListContainers(_a0 context.Context, _a1 Filter) ([]Container, error) { - ret := _m.Called(_a0, _a1) +// ListContainers provides a mock function with given fields: _a0, _a1, _a2 +func (_m *MockClient) ListContainers(_a0 context.Context, _a1 FilterFunc, _a2 ListOpts) ([]Container, error) { + ret := _m.Called(_a0, _a1, _a2) var r0 []Container - if rf, ok := ret.Get(0).(func(context.Context, Filter) []Container); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, FilterFunc, ListOpts) []Container); ok { + r0 = rf(_a0, _a1, _a2) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]Container) @@ -63,8 +44,8 @@ func (_m *MockClient) ListContainers(_a0 context.Context, _a1 Filter) ([]Contain } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, Filter) error); ok { - r1 = rf(_a0, _a1) + if rf, ok := ret.Get(1).(func(context.Context, FilterFunc, ListOpts) error); ok { + r1 = rf(_a0, _a1, _a2) } else { r1 = ret.Error(1) } diff --git a/pkg/container/test_helper.go b/pkg/container/test_helper.go index 3812ea2b..7cab621b 100644 --- a/pkg/container/test_helper.go +++ b/pkg/container/test_helper.go @@ -14,3 +14,14 @@ func CreateTestContainers(count int) []Container { } return containers } + +func CreateLabeledTestContainers(count int, labels map[string]string) []Container { + containers := []Container{} + for i := 0; i < count; i++ { + containers = append(containers, *NewContainer( + ContainerDetailsResponse(AsMap("Name", fmt.Sprintf("c%d", i), "Labels", labels)), + ImageDetailsResponse(AsMap()), + )) + } + return containers +} diff --git a/pkg/container/util.go b/pkg/container/util.go index cf50122a..fc823f34 100644 --- a/pkg/container/util.go +++ b/pkg/container/util.go @@ -7,65 +7,71 @@ import ( "time" ) -// AllContainersFilter all containers beside Pumba and PumbaSkip -func AllContainersFilter(c Container) bool { - if c.IsPumba() || c.IsPumbaSkip() { - return false - } - return true +type ListOpts struct { + All bool + Labels []string +} + +type Filter struct { + Names []string + Pattern string + Opts ListOpts } -func ContainerFilter(names []string) Filter { - if len(names) == 0 { - return AllContainersFilter +func matchNames(names []string, containerName string) bool { + for _, name := range names { + // container name may start with forward slash, when using inspect function + if (name == containerName) || (name == containerName[1:]) { + return true + } } + return false +} - return func(c Container) bool { - if c.IsPumba() || c.IsPumbaSkip() { +func matchPattern(pattern string, containerName string) bool { + matched, err := regexp.MatchString(pattern, containerName) + if err != nil { + return false + } + // container name may start with forward slash, when using inspect function + if !matched { + matched, err = regexp.MatchString(pattern, containerName[1:]) + if err != nil { return false } - for _, name := range names { - if (name == c.Name()) || (name == c.Name()[1:]) { - return true - } - } - return false } + return matched } -func RegexContainerFilter(pattern string) Filter { +func applyContainerFilter(filter Filter) FilterFunc { return func(c Container) bool { + // skip Pumba label if c.IsPumba() || c.IsPumbaSkip() { return false } - matched, err := regexp.MatchString(pattern, c.Name()) - if err != nil { - return false - } - // container name may start with forward slash, when using inspect function - if !matched { - matched, err = regexp.MatchString(pattern, c.Name()[1:]) - if err != nil { - return false + // if not requested all + if !filter.Opts.All { + // match names + if len(filter.Names) > 0 { + return matchNames(filter.Names, c.containerInfo.Name) + } else { // or regex pattern + return matchPattern(filter.Pattern, c.containerInfo.Name) } } - return matched + return true } } -func ListContainers(ctx context.Context, client Client, names []string, pattern string, all bool) ([]Container, error) { - var filter Filter - - if pattern != "" { - filter = RegexContainerFilter(pattern) - } else { - filter = ContainerFilter(names) - } - - if all { - return client.ListAllContainers(ctx, filter) +func listContainers(ctx context.Context, client Client, names []string, pattern string, labels []string, all bool) ([]Container, error) { + filter := Filter{ + Names: names, + Pattern: pattern, + Opts: ListOpts{ + All: all, + Labels: labels, + }, } - return client.ListContainers(ctx, filter) + return client.ListContainers(ctx, applyContainerFilter(filter), filter.Opts) } func RandomContainer(containers []Container) *Container { @@ -77,12 +83,8 @@ func RandomContainer(containers []Container) *Container { return nil } -func ListRunningContainers(ctx context.Context, client Client, names []string, pattern string) ([]Container, error) { - return ListContainers(ctx, client, names, pattern, false) -} - -func ListNContainers(ctx context.Context, client Client, names []string, pattern string, limit int) ([]Container, error) { - containers, err := ListRunningContainers(ctx, client, names, pattern) +func ListNContainers(ctx context.Context, client Client, names []string, pattern string, labels []string, limit int) ([]Container, error) { + containers, err := listContainers(ctx, client, names, pattern, labels, false) if err != nil { return nil, err } diff --git a/tests/kill_container.bats b/tests/kill_container.bats index 1f3fad3b..57d60ac9 100644 --- a/tests/kill_container.bats +++ b/tests/kill_container.bats @@ -16,4 +16,30 @@ # cleanup docker rm -f killing_victim || true +} + +@test "Should kill running labeled container with default signal" { + # given (started containers) + docker run -dit --label test=true --name killing_victim_1 alpine tail -f /dev/null + docker run -dit --label test=true --name killing_victim_2 alpine tail -f /dev/null + docker run -dit --label test=false --name killing_victim_3 alpine tail -f /dev/null + + # when (trying to kill container) + run pumba --label test=true kill "re2:^killing_victim*" + + # then (pumba exited successfully) + [ $status -eq 0 ] + + # and (container has been killed) + run docker inspect -f {{.State.Status}} killing_victim_1 + [[ $output == "exited" ]] + # and (container has been killed) + run docker inspect -f {{.State.Status}} killing_victim_2 + [[ $output == "exited" ]] + # and (container has not been killed) + run docker inspect -f {{.State.Status}} killing_victim_3 + [[ $output == "running" ]] + + # cleanup + docker rm -f killing_victim_1 killing_victim_2 killing_victim_3 || true } \ No newline at end of file