mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-13 21:56:38 +01:00
test: refactor client tests
should not be explicitly telling what they are testing and the requirements fixed the test data so that it doesn't contain discrepancies fully reset the mock client (no shared state) and only support the calls that is expected
This commit is contained in:
parent
3bf934027f
commit
26fba69169
5 changed files with 537 additions and 163 deletions
|
|
@ -3,91 +3,123 @@ package mocks
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/ghttp"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// NewMockAPIServer returns a mocked docker api server that responds to some fixed requests
|
||||
// used in the test suite.
|
||||
func NewMockAPIServer() *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.Debug("Mock server has received a HTTP call on ", r.URL)
|
||||
var response = ""
|
||||
|
||||
if isRequestFor("filters=", r) {
|
||||
|
||||
Filters := r.URL.Query().Get("filters")
|
||||
var result map[string]interface{}
|
||||
_ = json.Unmarshal([]byte(Filters), &result)
|
||||
status := result["status"].(map[string]interface{})
|
||||
|
||||
response = getMockJSONFromDisk("./mocks/data/containers.json")
|
||||
var x2 []types.Container
|
||||
var containers []types.Container
|
||||
_ = json.Unmarshal([]byte(response), &containers)
|
||||
for _, v := range containers {
|
||||
for key := range status {
|
||||
if v.State == key {
|
||||
x2 = append(x2, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(x2)
|
||||
response = string(b)
|
||||
|
||||
} else if isRequestFor("containers/json?limit=0", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/containers.json")
|
||||
} else if isRequestFor("ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/container_stopped.json")
|
||||
} else if isRequestFor("b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/container_running.json")
|
||||
} else if isRequestFor("ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/container_restarting.json")
|
||||
} else if isRequestFor("sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/image01.json")
|
||||
} else if isRequestFor("sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa", r) {
|
||||
response = getMockJSONFromDisk("./mocks/data/image02.json")
|
||||
} else if isRequestFor("containers/ex-cont-id/exec", r) {
|
||||
response = `{"Id": "ex-exec-id"}`
|
||||
} else if isRequestFor("exec/ex-exec-id/start", r) {
|
||||
response = `{"Id": "ex-exec-id"}`
|
||||
} else if isRequestFor("exec/ex-exec-id/json", r) {
|
||||
response = `{
|
||||
"ExecID": "ex-exec-id",
|
||||
"ContainerID": "ex-cont-id",
|
||||
"Running": false,
|
||||
"ExitCode": 0,
|
||||
"Pid": 0
|
||||
}`
|
||||
} else {
|
||||
// Allow ginkgo to correctly capture the failed assertion, even though this is called from a go func
|
||||
defer ginkgo.GinkgoRecover()
|
||||
ginkgo.Fail(fmt.Sprintf("mock API server endpoint not supported: %q", r.URL.String()))
|
||||
}
|
||||
_, _ = fmt.Fprintln(w, response)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
func isRequestFor(urlPart string, r *http.Request) bool {
|
||||
return strings.Contains(r.URL.String(), urlPart)
|
||||
}
|
||||
|
||||
func getMockJSONFromDisk(relPath string) string {
|
||||
func getMockJSONFile(relPath string) ([]byte, error) {
|
||||
absPath, _ := filepath.Abs(relPath)
|
||||
buf, err := ioutil.ReadFile(absPath)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithField("file", absPath).Error(err)
|
||||
return ""
|
||||
// logrus.WithError(err).WithField("file", absPath).Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return string(buf)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func RespondWithJSONFile(relPath string, statusCode int, optionalHeader ...http.Header) http.HandlerFunc {
|
||||
handler, err := respondWithJSONFile(relPath, statusCode, optionalHeader...)
|
||||
ExpectWithOffset(1, err).ShouldNot(HaveOccurred())
|
||||
return handler
|
||||
}
|
||||
|
||||
func respondWithJSONFile(relPath string, statusCode int, optionalHeader ...http.Header) (http.HandlerFunc, error) {
|
||||
buf, err := getMockJSONFile(relPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ghttp.RespondWith(statusCode, buf, optionalHeader...), nil
|
||||
}
|
||||
|
||||
func GetContainerHandlers(containerFiles ...string) []http.HandlerFunc {
|
||||
handlers := make([]http.HandlerFunc, 0, len(containerFiles)*2)
|
||||
for _, file := range containerFiles {
|
||||
handlers = append(handlers, getContainerHandler(file))
|
||||
|
||||
// Also append the image request since that will be called for every container
|
||||
if file == "running" {
|
||||
// The "running" container is the only one using image02
|
||||
handlers = append(handlers, getImageHandler(1))
|
||||
} else {
|
||||
handlers = append(handlers, getImageHandler(0))
|
||||
}
|
||||
}
|
||||
return handlers
|
||||
}
|
||||
|
||||
func createFilterArgs(statuses []string) filters.Args {
|
||||
args := filters.NewArgs()
|
||||
for _, status := range statuses {
|
||||
args.Add("status", status)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
var containerFileIds = map[string]string{
|
||||
"stopped": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65",
|
||||
"watchtower": "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
|
||||
"running": "b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008",
|
||||
"restarting": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67",
|
||||
}
|
||||
|
||||
var imageIds = []string{
|
||||
"sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa",
|
||||
"sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd",
|
||||
}
|
||||
|
||||
func getContainerHandler(file string) http.HandlerFunc {
|
||||
id, ok := containerFileIds[file]
|
||||
failTestUnless(ok)
|
||||
return ghttp.CombineHandlers(
|
||||
ghttp.VerifyRequest("GET", HaveSuffix("/containers/%v/json", id)),
|
||||
RespondWithJSONFile(fmt.Sprintf("./mocks/data/container_%v.json", file), http.StatusOK),
|
||||
)
|
||||
}
|
||||
|
||||
func ListContainersHandler(statuses ...string) http.HandlerFunc {
|
||||
filterArgs := createFilterArgs(statuses)
|
||||
bytes, err := filterArgs.MarshalJSON()
|
||||
ExpectWithOffset(1, err).ShouldNot(HaveOccurred())
|
||||
query := url.Values{
|
||||
"limit": []string{"0"},
|
||||
"filters": []string{string(bytes)},
|
||||
}
|
||||
return ghttp.CombineHandlers(
|
||||
ghttp.VerifyRequest("GET", HaveSuffix("containers/json"), query.Encode()),
|
||||
respondWithFilteredContainers(filterArgs),
|
||||
)
|
||||
}
|
||||
|
||||
func respondWithFilteredContainers(filters filters.Args) http.HandlerFunc {
|
||||
containersJson, err := getMockJSONFile("./mocks/data/containers.json")
|
||||
ExpectWithOffset(2, err).ShouldNot(HaveOccurred())
|
||||
var filteredContainers []types.Container
|
||||
var containers []types.Container
|
||||
ExpectWithOffset(2, json.Unmarshal(containersJson, &containers)).To(Succeed())
|
||||
for _, v := range containers {
|
||||
for _, key := range filters.Get("status") {
|
||||
if v.State == key {
|
||||
filteredContainers = append(filteredContainers, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ghttp.RespondWithJSONEncoded(http.StatusOK, filteredContainers)
|
||||
}
|
||||
|
||||
func getImageHandler(index int) http.HandlerFunc {
|
||||
return ghttp.CombineHandlers(
|
||||
ghttp.VerifyRequest("GET", HaveSuffix("/images/%v/json", imageIds[index])),
|
||||
RespondWithJSONFile(fmt.Sprintf("./mocks/data/image%02d.json", index+1), http.StatusOK),
|
||||
)
|
||||
}
|
||||
|
||||
func failTestUnless(ok bool) {
|
||||
ExpectWithOffset(2, ok).To(BeTrue(), "test setup failed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"HostnamePath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/hostname",
|
||||
"HostsPath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/hosts",
|
||||
"LogPath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65-json.log",
|
||||
"Name": "/watchtower-test",
|
||||
"Name": "/watchtower-stopped",
|
||||
"RestartCount": 0,
|
||||
"Driver": "overlay2",
|
||||
"Platform": "linux",
|
||||
|
|
|
|||
205
pkg/container/mocks/data/container_watchtower.json
Normal file
205
pkg/container/mocks/data/container_watchtower.json
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
{
|
||||
"Id": "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
|
||||
"Created": "2020-04-10T19:51:22.245041005Z",
|
||||
"Path": "/watchtower",
|
||||
"Args": [],
|
||||
"State": {
|
||||
"Status": "running",
|
||||
"Running": true,
|
||||
"Paused": false,
|
||||
"Restarting": false,
|
||||
"OOMKilled": false,
|
||||
"Dead": false,
|
||||
"Pid": 3854,
|
||||
"ExitCode": 0,
|
||||
"Error": "",
|
||||
"StartedAt": "2019-04-13T22:38:24.498745809Z",
|
||||
"FinishedAt": "2019-04-13T22:38:18.486292076Z"
|
||||
},
|
||||
"Image": "sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa",
|
||||
"ResolvConfPath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/resolv.conf",
|
||||
"HostnamePath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/hostname",
|
||||
"HostsPath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/hosts",
|
||||
"LogPath": "/var/lib/docker/containers/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65/ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65-json.log",
|
||||
"Name": "/watchtower-running",
|
||||
"RestartCount": 0,
|
||||
"Driver": "overlay2",
|
||||
"Platform": "linux",
|
||||
"MountLabel": "",
|
||||
"ProcessLabel": "",
|
||||
"AppArmorProfile": "",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
"/var/run/docker.sock:/var/run/docker.sock"
|
||||
],
|
||||
"ContainerIDFile": "",
|
||||
"LogConfig": {
|
||||
"Type": "json-file",
|
||||
"Config": {}
|
||||
},
|
||||
"NetworkMode": "default",
|
||||
"PortBindings": {},
|
||||
"RestartPolicy": {
|
||||
"Name": "no",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
"AutoRemove": false,
|
||||
"VolumeDriver": "",
|
||||
"VolumesFrom": null,
|
||||
"CapAdd": null,
|
||||
"CapDrop": null,
|
||||
"Dns": [],
|
||||
"DnsOptions": [],
|
||||
"DnsSearch": [],
|
||||
"ExtraHosts": null,
|
||||
"GroupAdd": null,
|
||||
"IpcMode": "shareable",
|
||||
"Cgroup": "",
|
||||
"Links": null,
|
||||
"OomScoreAdj": 0,
|
||||
"PidMode": "",
|
||||
"Privileged": false,
|
||||
"PublishAllPorts": false,
|
||||
"ReadonlyRootfs": false,
|
||||
"SecurityOpt": null,
|
||||
"UTSMode": "",
|
||||
"UsernsMode": "",
|
||||
"ShmSize": 67108864,
|
||||
"Runtime": "runc",
|
||||
"ConsoleSize": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Isolation": "",
|
||||
"CpuShares": 0,
|
||||
"Memory": 0,
|
||||
"NanoCpus": 0,
|
||||
"CgroupParent": "",
|
||||
"BlkioWeight": 0,
|
||||
"BlkioWeightDevice": [],
|
||||
"BlkioDeviceReadBps": null,
|
||||
"BlkioDeviceWriteBps": null,
|
||||
"BlkioDeviceReadIOps": null,
|
||||
"BlkioDeviceWriteIOps": null,
|
||||
"CpuPeriod": 0,
|
||||
"CpuQuota": 0,
|
||||
"CpuRealtimePeriod": 0,
|
||||
"CpuRealtimeRuntime": 0,
|
||||
"CpusetCpus": "",
|
||||
"CpusetMems": "",
|
||||
"Devices": [],
|
||||
"DeviceCgroupRules": null,
|
||||
"DiskQuota": 0,
|
||||
"KernelMemory": 0,
|
||||
"MemoryReservation": 0,
|
||||
"MemorySwap": 0,
|
||||
"MemorySwappiness": null,
|
||||
"OomKillDisable": false,
|
||||
"PidsLimit": 0,
|
||||
"Ulimits": null,
|
||||
"CpuCount": 0,
|
||||
"CpuPercent": 0,
|
||||
"IOMaximumIOps": 0,
|
||||
"IOMaximumBandwidth": 0,
|
||||
"MaskedPaths": [
|
||||
"/proc/asound",
|
||||
"/proc/acpi",
|
||||
"/proc/kcore",
|
||||
"/proc/keys",
|
||||
"/proc/latency_stats",
|
||||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware"
|
||||
],
|
||||
"ReadonlyPaths": [
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger"
|
||||
]
|
||||
},
|
||||
"GraphDriver": {
|
||||
"Data": {
|
||||
"LowerDir": "/var/lib/docker/overlay2/9f6b91ea6e142835035d91123bbc7a05224dfa2abd4d020eac42f2ab420ccddc-init/diff:/var/lib/docker/overlay2/cdf82f50bc49177d0c17c24f3eaa29eba607b70cc6a081f77781b21c59a13eb8/diff:/var/lib/docker/overlay2/8108325ee844603c9b08d2772cf6e65dccf31dd5171f265078e5ed79a0ba3c0f/diff:/var/lib/docker/overlay2/e5e0cce6bf91b829a308424d99d7e56a33be3a11414ff5cdc48e762a1342b20f/diff",
|
||||
"MergedDir": "/var/lib/docker/overlay2/9f6b91ea6e142835035d91123bbc7a05224dfa2abd4d020eac42f2ab420ccddc/merged",
|
||||
"UpperDir": "/var/lib/docker/overlay2/9f6b91ea6e142835035d91123bbc7a05224dfa2abd4d020eac42f2ab420ccddc/diff",
|
||||
"WorkDir": "/var/lib/docker/overlay2/9f6b91ea6e142835035d91123bbc7a05224dfa2abd4d020eac42f2ab420ccddc/work"
|
||||
},
|
||||
"Name": "overlay2"
|
||||
},
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/var/run/docker.sock",
|
||||
"Destination": "/var/run/docker.sock",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
],
|
||||
"Config": {
|
||||
"Hostname": "ae8964ba86c7",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": true,
|
||||
"AttachStderr": true,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
],
|
||||
"Cmd": null,
|
||||
"Image": "containrrr/watchtower:latest",
|
||||
"Volumes": null,
|
||||
"WorkingDir": "",
|
||||
"Entrypoint": [
|
||||
"/watchtower"
|
||||
],
|
||||
"OnBuild": null,
|
||||
"Labels": {
|
||||
"com.centurylinklabs.watchtower": "true"
|
||||
}
|
||||
},
|
||||
"NetworkSettings": {
|
||||
"Bridge": "",
|
||||
"SandboxID": "05627d36c08ed994eebc44a2a8c9365a511756b55c500fb03fd5a14477cd4bf3",
|
||||
"HairpinMode": false,
|
||||
"LinkLocalIPv6Address": "",
|
||||
"LinkLocalIPv6PrefixLen": 0,
|
||||
"Ports": {},
|
||||
"SandboxKey": "/var/run/docker/netns/05627d36c08e",
|
||||
"SecondaryIPAddresses": null,
|
||||
"SecondaryIPv6Addresses": null,
|
||||
"EndpointID": "",
|
||||
"Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAddress": "",
|
||||
"IPPrefixLen": 0,
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "",
|
||||
"Networks": {
|
||||
"bridge": {
|
||||
"IPAMConfig": null,
|
||||
"Links": null,
|
||||
"Aliases": null,
|
||||
"NetworkID": "8fcfd56fa9203bafa98510abb08bff66ad05bef5b6e97d158cbae3397e1e065e",
|
||||
"EndpointID": "",
|
||||
"Gateway": "",
|
||||
"IPAddress": "",
|
||||
"IPPrefixLen": 0,
|
||||
"IPv6Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"MacAddress": "",
|
||||
"DriverOpts": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,55 @@
|
|||
{
|
||||
"Id": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65",
|
||||
"Names": [
|
||||
"/watchtower-test"
|
||||
"/watchtower-stopped"
|
||||
],
|
||||
"Image": "containrrr/watchtower:latest",
|
||||
"ImageID": "sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa",
|
||||
"Command": "/watchtower",
|
||||
"Created": 1554925882,
|
||||
"Ports": [],
|
||||
"Labels": {
|
||||
"com.centurylinklabs.watchtower": "true"
|
||||
},
|
||||
"State": "exited",
|
||||
"Status": "Exited (1) 6 days ago",
|
||||
"HostConfig": {
|
||||
"NetworkMode": "default"
|
||||
},
|
||||
"NetworkSettings": {
|
||||
"Networks": {
|
||||
"bridge": {
|
||||
"IPAMConfig": null,
|
||||
"Links": null,
|
||||
"Aliases": null,
|
||||
"NetworkID": "8fcfd56fa9203bafa98510abb08bff66ad05bef5b6e97d158cbae3397e1e065e",
|
||||
"EndpointID": "",
|
||||
"Gateway": "",
|
||||
"IPAddress": "",
|
||||
"IPPrefixLen": 0,
|
||||
"IPv6Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"MacAddress": "",
|
||||
"DriverOpts": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/var/run/docker.sock",
|
||||
"Destination": "/var/run/docker.sock",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
|
||||
"Names": [
|
||||
"/watchtower-running"
|
||||
],
|
||||
"Image": "containrrr/watchtower:latest",
|
||||
"ImageID": "sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa",
|
||||
|
|
@ -13,7 +61,7 @@
|
|||
"com.centurylinklabs.watchtower": "true"
|
||||
},
|
||||
"State": "running",
|
||||
"Status": "Exited (1) 6 days ago",
|
||||
"Status": "Up 3 days",
|
||||
"HostConfig": {
|
||||
"NetworkMode": "default"
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue