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:
schizo99 2023-08-08 18:32:44 +02:00 committed by GitHub
parent bba9b2b100
commit dca45f50cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1379 additions and 47 deletions

View file

@ -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)