supabase-cli/internal/testing/apitest/docker.go

122 lines
4.0 KiB
Go

package apitest
import (
"bytes"
"fmt"
"net/http"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/go-errors/errors"
"github.com/h2non/gock"
)
const mockHost = "http://127.0.0.1"
func MockDocker(docker *client.Client) error {
// Skip setup if docker is already mocked
if docker.DaemonHost() == mockHost {
return nil
}
if err := client.WithVersion(api.DefaultVersion)(docker); err != nil {
return err
}
// Safe to ignore errors as transport will be replaced by gock
_ = client.WithHost(mockHost)(docker)
return client.WithHTTPClient(http.DefaultClient)(docker)
}
// Ref: internal/utils/docker.go::DockerStart
func MockDockerStart(docker *client.Client, image, containerID string) {
gock.New(docker.DaemonHost()).
Get("/v" + docker.ClientVersion() + "/images/" + image + "/json").
Reply(http.StatusOK).
JSON(types.ImageInspect{})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/networks/create").
Reply(http.StatusCreated).
JSON(network.CreateResponse{})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/volumes/create").
Persist().
Reply(http.StatusCreated).
JSON(volume.Volume{})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/containers/create").
Reply(http.StatusOK).
JSON(container.CreateResponse{ID: containerID})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/containers/" + containerID + "/start").
Reply(http.StatusAccepted)
}
// Ref: internal/utils/docker.go::DockerRemoveAll
func MockDockerStop(docker *client.Client) {
gock.New(docker.DaemonHost()).
Get("/v" + docker.ClientVersion() + "/containers/json").
Reply(http.StatusOK).
JSON([]types.Container{})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/containers/prune").
Reply(http.StatusOK).
JSON(container.PruneReport{})
if !versions.GreaterThanOrEqualTo(docker.ClientVersion(), "1.42") {
gock.New(docker.DaemonHost()).
Post("/v"+docker.ClientVersion()+"/volumes/prune").
MatchParam("filters", `"all":{"true":true}`).
ReplyError(errors.New(`failed to parse filters for all=true&label=com.supabase.cli.project%3Dtest: "all" is an invalid volume filter`))
}
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/volumes/prune").
Reply(http.StatusOK).
JSON(volume.PruneReport{})
gock.New(docker.DaemonHost()).
Post("/v" + docker.ClientVersion() + "/networks/prune").
Reply(http.StatusOK).
JSON(network.PruneReport{})
}
// Ref: internal/utils/docker.go::DockerRunOnce
func setupDockerLogs(docker *client.Client, containerID, stdout string, exitCode int) error {
var body bytes.Buffer
writer := stdcopy.NewStdWriter(&body, stdcopy.Stdout)
_, err := writer.Write([]byte(stdout))
gock.New(docker.DaemonHost()).
Get("/v"+docker.ClientVersion()+"/containers/"+containerID+"/logs").
Reply(http.StatusOK).
SetHeader("Content-Type", "application/vnd.docker.raw-stream").
Body(&body)
gock.New(docker.DaemonHost()).
Get("/v" + docker.ClientVersion() + "/containers/" + containerID + "/json").
Reply(http.StatusOK).
JSON(types.ContainerJSONBase{State: &types.ContainerState{
ExitCode: exitCode,
}})
gock.New(docker.DaemonHost()).
Delete("/v" + docker.ClientVersion() + "/containers/" + containerID).
Reply(http.StatusOK)
return err
}
func MockDockerLogs(docker *client.Client, containerID, stdout string) error {
return setupDockerLogs(docker, containerID, stdout, 0)
}
func MockDockerLogsExitCode(docker *client.Client, containerID string, exitCode int) error {
return setupDockerLogs(docker, containerID, "", exitCode)
}
func ListUnmatchedRequests() []string {
result := make([]string, len(gock.GetUnmatchedRequests()))
for i, r := range gock.GetUnmatchedRequests() {
result[i] = fmt.Sprintln(r.Method, r.URL.Path)
}
return result
}