You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
watchtower/pkg/container/mocks/ApiServer.go

220 lines
7.5 KiB
Go

package mocks
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
O "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
)
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 nil, err
}
return buf, nil
}
// RespondWithJSONFile handles a request by returning the contents of the supplied file
func RespondWithJSONFile(relPath string, statusCode int, optionalHeader ...http.Header) http.HandlerFunc {
handler, err := respondWithJSONFile(relPath, statusCode, optionalHeader...)
O.ExpectWithOffset(1, err).ShouldNot(O.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
}
// GetContainerHandlers returns the handlers serving lookups for the supplied container mock files
func GetContainerHandlers(containerFiles ...string) []http.HandlerFunc {
handlers := make([]http.HandlerFunc, 0, len(containerFiles)*2)
for _, file := range containerFiles {
handlers = append(handlers, getContainerFileHandler(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, getImageFileHandler(1))
} else {
handlers = append(handlers, getImageFileHandler(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 getContainerFileHandler(file string) http.HandlerFunc {
id, ok := containerFileIds[file]
failTestUnless(ok)
return getContainerHandler(
id,
RespondWithJSONFile(fmt.Sprintf("./mocks/data/container_%v.json", file), http.StatusOK),
)
}
func getContainerHandler(containerId string, responseHandler http.HandlerFunc) http.HandlerFunc {
return ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", O.HaveSuffix("/containers/%v/json", containerId)),
responseHandler,
)
}
// GetContainerHandler mocks the GET containers/{containerID}/json endpoint.
//
// If containerInfo is nil, it returns an 200 OK http result with the containerInfo as the body.
// Otherwise, it returns the appropriate 404 NotFound result for the containerID.
func GetContainerHandler(containerID string, containerInfo *types.ContainerJSON) http.HandlerFunc {
responseHandler := containerNotFoundResponse(containerID)
if containerInfo != nil {
responseHandler = ghttp.RespondWithJSONEncoded(http.StatusOK, containerInfo)
}
return getContainerHandler(containerID, responseHandler)
}
// GetContainerHandlerOK mocks the GET containers/{containerInfo.ID}/json endpoint, returning the containerInfo
func GetContainerHandlerOK(containerInfo *types.ContainerJSON) http.HandlerFunc {
O.ExpectWithOffset(1, containerInfo).ToNot(O.BeNil())
return GetContainerHandler(containerInfo.ID, containerInfo)
}
// GetImageHandlerOK mocks the GET images/{imageName}/json endpoint, returning a 200 OK response with the imageInfo
func GetImageHandlerOK(imageName string, imageInfo *types.ImageInspect) http.HandlerFunc {
return getImageHandler(imageName, ghttp.RespondWithJSONEncoded(http.StatusOK, imageInfo))
}
// GetImageHandlerNotFound mocks the GET images/{imageName}/json endpoint, returning a 404 NotFound response
// with the appropriate error message for imageName
func GetImageHandlerNotFound(imageName string) http.HandlerFunc {
body := errorMessage{"no such image: " + imageName}
return getImageHandler(imageName, ghttp.RespondWithJSONEncoded(http.StatusNotFound, body))
}
// PullImageHandlerOK mocks the POST images/create endpoint, returning a 204 NoContent response
func PullImageHandlerOK() http.HandlerFunc {
return ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", O.HaveSuffix("images/create")),
ghttp.RespondWith(http.StatusNoContent, nil),
)
}
// ListContainersHandler mocks the GET containers/json endpoint, filtering the returned containers based on statuses
func ListContainersHandler(statuses ...string) http.HandlerFunc {
filterArgs := createFilterArgs(statuses)
bytes, err := filterArgs.MarshalJSON()
O.ExpectWithOffset(1, err).ShouldNot(O.HaveOccurred())
query := url.Values{
"limit": []string{"0"},
"filters": []string{string(bytes)},
}
return ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", O.HaveSuffix("containers/json"), query.Encode()),
respondWithFilteredContainers(filterArgs),
)
}
func respondWithFilteredContainers(filters filters.Args) http.HandlerFunc {
containersJSON, err := getMockJSONFile("./mocks/data/containers.json")
O.ExpectWithOffset(2, err).ShouldNot(O.HaveOccurred())
var filteredContainers []types.Container
var containers []types.Container
O.ExpectWithOffset(2, json.Unmarshal(containersJSON, &containers)).To(O.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(imageId string, responseHandler http.HandlerFunc) http.HandlerFunc {
return ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", O.HaveSuffix("/images/%s/json", imageId)),
responseHandler,
)
}
func getImageFileHandler(index int) http.HandlerFunc {
return getImageHandler(imageIds[index],
RespondWithJSONFile(fmt.Sprintf("./mocks/data/image%02d.json", index+1), http.StatusOK),
)
}
func failTestUnless(ok bool) {
O.ExpectWithOffset(2, ok).To(O.BeTrue(), "test setup failed")
}
// KillContainerHandler mocks the POST containers/{id}/kill endpoint
func KillContainerHandler(containerID string, found FoundStatus) http.HandlerFunc {
responseHandler := noContentStatusResponse
if !found {
responseHandler = containerNotFoundResponse(containerID)
}
return ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", O.HaveSuffix("containers/%s/kill", containerID)),
responseHandler,
)
}
// RemoveContainerHandler mocks the DELETE containers/{id} endpoint
func RemoveContainerHandler(containerID string, found FoundStatus) http.HandlerFunc {
responseHandler := noContentStatusResponse
if !found {
responseHandler = containerNotFoundResponse(containerID)
}
return ghttp.CombineHandlers(
ghttp.VerifyRequest("DELETE", O.HaveSuffix("containers/%s", containerID)),
responseHandler,
)
}
type errorMessage struct{ message string }
func containerNotFoundResponse(containerID string) http.HandlerFunc {
return ghttp.RespondWithJSONEncoded(http.StatusNotFound, errorMessage{"No such container: " + containerID})
}
var noContentStatusResponse = ghttp.RespondWith(http.StatusNoContent, nil)
type FoundStatus bool
const (
Found FoundStatus = true
Missing FoundStatus = false
)