mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-14 06:06:38 +01:00
feat: support container network mode (#1429)
Co-authored-by: nils måsén <nils@piksel.se> Co-authored-by: Andreas Åhman <andreas.ahman@ingka.ikea.com>
This commit is contained in:
parent
bba9b2b100
commit
dca45f50cb
15 changed files with 1379 additions and 47 deletions
|
|
@ -3,12 +3,15 @@ package mocks
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"github.com/onsi/ginkgo"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
t "github.com/containrrr/watchtower/pkg/types"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
O "github.com/onsi/gomega"
|
||||
|
|
@ -17,10 +20,9 @@ import (
|
|||
|
||||
func getMockJSONFile(relPath string) ([]byte, error) {
|
||||
absPath, _ := filepath.Abs(relPath)
|
||||
buf, err := ioutil.ReadFile(absPath)
|
||||
buf, err := os.ReadFile(absPath)
|
||||
if err != nil {
|
||||
// logrus.WithError(err).WithField("file", absPath).Error(err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("mock JSON file %q not found: %e", absPath, err)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
|
@ -41,19 +43,22 @@ func respondWithJSONFile(relPath string, statusCode int, optionalHeader ...http.
|
|||
}
|
||||
|
||||
// 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))
|
||||
func GetContainerHandlers(containerRefs ...*ContainerRef) []http.HandlerFunc {
|
||||
handlers := make([]http.HandlerFunc, 0, len(containerRefs)*3)
|
||||
for _, containerRef := range containerRefs {
|
||||
handlers = append(handlers, getContainerFileHandler(containerRef))
|
||||
|
||||
// Also append any containers that the container references, if any
|
||||
for _, ref := range containerRef.references {
|
||||
handlers = append(handlers, getContainerFileHandler(ref))
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
handlers = append(handlers, getImageHandler(containerRef.image.id,
|
||||
RespondWithJSONFile(containerRef.image.getFileName(), http.StatusOK),
|
||||
))
|
||||
}
|
||||
|
||||
return handlers
|
||||
}
|
||||
|
||||
|
|
@ -65,24 +70,90 @@ func createFilterArgs(statuses []string) filters.Args {
|
|||
return args
|
||||
}
|
||||
|
||||
var containerFileIds = map[string]string{
|
||||
"stopped": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65",
|
||||
"watchtower": "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
|
||||
"running": "b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008",
|
||||
"restarting": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67",
|
||||
var defaultImage = imageRef{
|
||||
// watchtower
|
||||
id: t.ImageID("sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa"),
|
||||
file: "default",
|
||||
}
|
||||
|
||||
var imageIds = []string{
|
||||
"sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa",
|
||||
"sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd",
|
||||
var Watchtower = ContainerRef{
|
||||
name: "watchtower",
|
||||
id: "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
|
||||
image: &defaultImage,
|
||||
}
|
||||
var Stopped = ContainerRef{
|
||||
name: "stopped",
|
||||
id: "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65",
|
||||
image: &defaultImage,
|
||||
}
|
||||
var Running = ContainerRef{
|
||||
name: "running",
|
||||
id: "b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008",
|
||||
image: &imageRef{
|
||||
// portainer
|
||||
id: t.ImageID("sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd"),
|
||||
file: "running",
|
||||
},
|
||||
}
|
||||
var Restarting = ContainerRef{
|
||||
name: "restarting",
|
||||
id: "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67",
|
||||
image: &defaultImage,
|
||||
}
|
||||
|
||||
func getContainerFileHandler(file string) http.HandlerFunc {
|
||||
id, ok := containerFileIds[file]
|
||||
failTestUnless(ok)
|
||||
var netSupplierOK = ContainerRef{
|
||||
id: "25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2",
|
||||
name: "net_supplier",
|
||||
image: &imageRef{
|
||||
// gluetun
|
||||
id: t.ImageID("sha256:c22b543d33bfdcb9992cbef23961677133cdf09da71d782468ae2517138bad51"),
|
||||
file: "net_producer",
|
||||
},
|
||||
}
|
||||
var netSupplierNotFound = ContainerRef{
|
||||
id: NetSupplierNotFoundID,
|
||||
name: netSupplierOK.name,
|
||||
isMissing: true,
|
||||
}
|
||||
|
||||
// NetConsumerOK is used for testing `container` networking mode
|
||||
// returns a container that consumes an existing supplier container
|
||||
var NetConsumerOK = ContainerRef{
|
||||
id: "1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6",
|
||||
name: "net_consumer",
|
||||
image: &imageRef{
|
||||
id: t.ImageID("sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8"), // nginx
|
||||
file: "net_consumer",
|
||||
},
|
||||
references: []*ContainerRef{&netSupplierOK},
|
||||
}
|
||||
|
||||
// NetConsumerInvalidSupplier is used for testing `container` networking mode
|
||||
// returns a container that references a supplying container that does not exist
|
||||
var NetConsumerInvalidSupplier = ContainerRef{
|
||||
id: NetConsumerOK.id,
|
||||
name: "net_consumer-missing_supplier",
|
||||
image: NetConsumerOK.image,
|
||||
references: []*ContainerRef{&netSupplierNotFound},
|
||||
}
|
||||
|
||||
const NetSupplierNotFoundID = "badc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc"
|
||||
const NetSupplierContainerName = "/wt-contnet-producer-1"
|
||||
|
||||
func getContainerFileHandler(cr *ContainerRef) http.HandlerFunc {
|
||||
|
||||
if cr.isMissing {
|
||||
return containerNotFoundResponse(string(cr.id))
|
||||
}
|
||||
|
||||
containerFile, err := cr.getContainerFile()
|
||||
if err != nil {
|
||||
ginkgo.Fail(fmt.Sprintf("Failed to get container mock file: %v", err))
|
||||
}
|
||||
|
||||
return getContainerHandler(
|
||||
id,
|
||||
RespondWithJSONFile(fmt.Sprintf("./mocks/data/container_%v.json", file), http.StatusOK),
|
||||
string(cr.id),
|
||||
RespondWithJSONFile(containerFile, http.StatusOK),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +175,7 @@ func GetContainerHandler(containerID string, containerInfo *types.ContainerJSON)
|
|||
|
||||
// GetImageHandler mocks the GET images/{id}/json endpoint
|
||||
func GetImageHandler(imageInfo *types.ImageInspect) http.HandlerFunc {
|
||||
return getImageHandler(imageInfo.ID, ghttp.RespondWithJSONEncoded(http.StatusOK, imageInfo))
|
||||
return getImageHandler(t.ImageID(imageInfo.ID), ghttp.RespondWithJSONEncoded(http.StatusOK, imageInfo))
|
||||
}
|
||||
|
||||
// ListContainersHandler mocks the GET containers/json endpoint, filtering the returned containers based on statuses
|
||||
|
|
@ -138,23 +209,13 @@ func respondWithFilteredContainers(filters filters.Args) http.HandlerFunc {
|
|||
return ghttp.RespondWithJSONEncoded(http.StatusOK, filteredContainers)
|
||||
}
|
||||
|
||||
func getImageHandler(imageId string, responseHandler http.HandlerFunc) http.HandlerFunc {
|
||||
func getImageHandler(imageId t.ImageID, 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
|
||||
|
|
@ -180,7 +241,7 @@ func RemoveContainerHandler(containerID string, found FoundStatus) http.HandlerF
|
|||
}
|
||||
|
||||
func containerNotFoundResponse(containerID string) http.HandlerFunc {
|
||||
return ghttp.RespondWithJSONEncoded(http.StatusNotFound, struct{ message string }{message: "No such container: " + containerID})
|
||||
return ghttp.RespondWithJSONEncoded(http.StatusNotFound, struct{ message string }{message: "No such container: " + string(containerID)})
|
||||
}
|
||||
|
||||
var noContentStatusResponse = ghttp.RespondWith(http.StatusNoContent, nil)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue