feat: automatically start podman service (#648)

* feat: automatically start podman service

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* src: refactor rename function

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* src: add commnets to tests

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* src: refactor rename function

Signed-off-by: Matej Vasek <mvasek@redhat.com>
This commit is contained in:
Matej Vasek 2021-11-11 18:38:38 +01:00 committed by GitHub
parent e20ac4c6b7
commit bfdfb760cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 794 additions and 6 deletions

View File

@ -2,3 +2,4 @@ ignore:
- third_party
- vendor
- testdata
- docker/docker_client_generated.go

View File

@ -11,6 +11,8 @@ import (
"runtime"
"strings"
"github.com/docker/docker/client"
fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/docker"
@ -79,10 +81,11 @@ func (builder *Builder) Build(ctx context.Context, f fn.Function) (err error) {
logWriter = &bytes.Buffer{}
}
cli, dockerHost, err := docker.NewDockerClient()
cli, dockerHost, err := docker.NewClient(client.DefaultDockerHost)
if err != nil {
return err
}
defer cli.Close()
version, err := cli.ServerVersion(ctx)
if err != nil {

View File

@ -1,18 +1,44 @@
package docker
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"time"
"knative.dev/kn-plugin-func/ssh"
"github.com/docker/docker/client"
)
func NewDockerClient() (dockerClient client.CommonAPIClient, dockerHost string, err error) {
func NewClient(defaultHost string) (dockerClient client.CommonAPIClient, dockerHost string, err error) {
var _url *url.URL
dockerHost = os.Getenv("DOCKER_HOST")
_url, err := url.Parse(dockerHost)
if dockerHost == "" && runtime.GOOS == "linux" && podmanPresent() {
_url, err = url.Parse(defaultHost)
if err != nil {
return
}
_, err = os.Stat(_url.Path)
switch {
case err != nil && !os.IsNotExist(err):
return
case os.IsNotExist(err):
dockerClient, dockerHost, err = newClientWithPodmanService()
return
}
}
_url, err = url.Parse(dockerHost)
isSSH := err == nil && _url.Scheme == "ssh"
if !isSSH {
@ -47,3 +73,64 @@ func NewDockerClient() (dockerClient client.CommonAPIClient, dockerHost string,
return dockerClient, dockerHost, err
}
func podmanPresent() bool {
_, err := exec.LookPath("podman")
return err == nil
}
// creates a docker client that has its own podman service associated with it
// the service is shutdown when Close() is called on the client
func newClientWithPodmanService() (dockerClient client.CommonAPIClient, dockerHost string, err error) {
tmpDir, err := os.MkdirTemp("", "func-podman-")
if err != nil {
return
}
podmanSocket := filepath.Join(tmpDir, "podman.sock")
dockerHost = fmt.Sprintf("unix://%s", podmanSocket)
cmd := exec.Command("podman", "system", "service", dockerHost, "--time=0")
err = cmd.Start()
if err != nil {
return
}
dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithHost(dockerHost), client.WithAPIVersionNegotiation())
stopPodmanService := func() {
_ = cmd.Process.Signal(syscall.SIGTERM)
_ = os.RemoveAll(tmpDir)
}
dockerClient = withPodman{
pimpl: dockerClient,
stopPodman: stopPodmanService,
}
podmanServiceRunning := false
// give a time to podman to start
for i := 0; i < 40; i++ {
if _, e := dockerClient.Ping(context.Background()); e == nil {
podmanServiceRunning = true
break
}
time.Sleep(time.Millisecond * 250)
}
if !podmanServiceRunning {
stopPodmanService()
err = errors.New("failed to start podman service")
}
return
}
type withPodman struct {
stopPodman func()
pimpl client.CommonAPIClient
}
// Close function need to stop associated podman service
func (w withPodman) Close() error {
defer w.stopPodman()
return w.pimpl.Close()
}

View File

@ -0,0 +1,488 @@
package docker
import (
"context"
"io"
"net"
"net/http"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/volume"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
//<editor-fold desc="plain forwards to pimpl">
func (w withPodman) BuildCachePrune(arg0 context.Context, arg1 types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
return w.pimpl.BuildCachePrune(arg0, arg1)
}
func (w withPodman) BuildCancel(arg0 context.Context, arg1 string) error {
return w.pimpl.BuildCancel(arg0, arg1)
}
func (w withPodman) ClientVersion() string {
return w.pimpl.ClientVersion()
}
func (w withPodman) ConfigCreate(arg0 context.Context, arg1 swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
return w.pimpl.ConfigCreate(arg0, arg1)
}
func (w withPodman) ConfigInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Config, []uint8, error) {
return w.pimpl.ConfigInspectWithRaw(arg0, arg1)
}
func (w withPodman) ConfigList(arg0 context.Context, arg1 types.ConfigListOptions) ([]swarm.Config, error) {
return w.pimpl.ConfigList(arg0, arg1)
}
func (w withPodman) ConfigRemove(arg0 context.Context, arg1 string) error {
return w.pimpl.ConfigRemove(arg0, arg1)
}
func (w withPodman) ConfigUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ConfigSpec) error {
return w.pimpl.ConfigUpdate(arg0, arg1, arg2, arg3)
}
func (w withPodman) ContainerAttach(arg0 context.Context, arg1 string, arg2 types.ContainerAttachOptions) (types.HijackedResponse, error) {
return w.pimpl.ContainerAttach(arg0, arg1, arg2)
}
func (w withPodman) ContainerCommit(arg0 context.Context, arg1 string, arg2 types.ContainerCommitOptions) (types.IDResponse, error) {
return w.pimpl.ContainerCommit(arg0, arg1, arg2)
}
func (w withPodman) ContainerCreate(arg0 context.Context, arg1 *container.Config, arg2 *container.HostConfig, arg3 *network.NetworkingConfig, arg4 *v1.Platform, arg5 string) (container.ContainerCreateCreatedBody, error) {
return w.pimpl.ContainerCreate(arg0, arg1, arg2, arg3, arg4, arg5)
}
func (w withPodman) ContainerDiff(arg0 context.Context, arg1 string) ([]container.ContainerChangeResponseItem, error) {
return w.pimpl.ContainerDiff(arg0, arg1)
}
func (w withPodman) ContainerExecAttach(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) (types.HijackedResponse, error) {
return w.pimpl.ContainerExecAttach(arg0, arg1, arg2)
}
func (w withPodman) ContainerExecCreate(arg0 context.Context, arg1 string, arg2 types.ExecConfig) (types.IDResponse, error) {
return w.pimpl.ContainerExecCreate(arg0, arg1, arg2)
}
func (w withPodman) ContainerExecInspect(arg0 context.Context, arg1 string) (types.ContainerExecInspect, error) {
return w.pimpl.ContainerExecInspect(arg0, arg1)
}
func (w withPodman) ContainerExecResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error {
return w.pimpl.ContainerExecResize(arg0, arg1, arg2)
}
func (w withPodman) ContainerExecStart(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) error {
return w.pimpl.ContainerExecStart(arg0, arg1, arg2)
}
func (w withPodman) ContainerExport(arg0 context.Context, arg1 string) (io.ReadCloser, error) {
return w.pimpl.ContainerExport(arg0, arg1)
}
func (w withPodman) ContainerInspect(arg0 context.Context, arg1 string) (types.ContainerJSON, error) {
return w.pimpl.ContainerInspect(arg0, arg1)
}
func (w withPodman) ContainerInspectWithRaw(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerJSON, []uint8, error) {
return w.pimpl.ContainerInspectWithRaw(arg0, arg1, arg2)
}
func (w withPodman) ContainerKill(arg0 context.Context, arg1 string, arg2 string) error {
return w.pimpl.ContainerKill(arg0, arg1, arg2)
}
func (w withPodman) ContainerList(arg0 context.Context, arg1 types.ContainerListOptions) ([]types.Container, error) {
return w.pimpl.ContainerList(arg0, arg1)
}
func (w withPodman) ContainerLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
return w.pimpl.ContainerLogs(arg0, arg1, arg2)
}
func (w withPodman) ContainerPause(arg0 context.Context, arg1 string) error {
return w.pimpl.ContainerPause(arg0, arg1)
}
func (w withPodman) ContainerRemove(arg0 context.Context, arg1 string, arg2 types.ContainerRemoveOptions) error {
return w.pimpl.ContainerRemove(arg0, arg1, arg2)
}
func (w withPodman) ContainerRename(arg0 context.Context, arg1 string, arg2 string) error {
return w.pimpl.ContainerRename(arg0, arg1, arg2)
}
func (w withPodman) ContainerResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error {
return w.pimpl.ContainerResize(arg0, arg1, arg2)
}
func (w withPodman) ContainerRestart(arg0 context.Context, arg1 string, arg2 *time.Duration) error {
return w.pimpl.ContainerRestart(arg0, arg1, arg2)
}
func (w withPodman) ContainerStart(arg0 context.Context, arg1 string, arg2 types.ContainerStartOptions) error {
return w.pimpl.ContainerStart(arg0, arg1, arg2)
}
func (w withPodman) ContainerStatPath(arg0 context.Context, arg1 string, arg2 string) (types.ContainerPathStat, error) {
return w.pimpl.ContainerStatPath(arg0, arg1, arg2)
}
func (w withPodman) ContainerStats(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerStats, error) {
return w.pimpl.ContainerStats(arg0, arg1, arg2)
}
func (w withPodman) ContainerStatsOneShot(arg0 context.Context, arg1 string) (types.ContainerStats, error) {
return w.pimpl.ContainerStatsOneShot(arg0, arg1)
}
func (w withPodman) ContainerStop(arg0 context.Context, arg1 string, arg2 *time.Duration) error {
return w.pimpl.ContainerStop(arg0, arg1, arg2)
}
func (w withPodman) ContainerTop(arg0 context.Context, arg1 string, arg2 []string) (container.ContainerTopOKBody, error) {
return w.pimpl.ContainerTop(arg0, arg1, arg2)
}
func (w withPodman) ContainerUnpause(arg0 context.Context, arg1 string) error {
return w.pimpl.ContainerUnpause(arg0, arg1)
}
func (w withPodman) ContainerUpdate(arg0 context.Context, arg1 string, arg2 container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
return w.pimpl.ContainerUpdate(arg0, arg1, arg2)
}
func (w withPodman) ContainerWait(arg0 context.Context, arg1 string, arg2 container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) {
return w.pimpl.ContainerWait(arg0, arg1, arg2)
}
func (w withPodman) ContainersPrune(arg0 context.Context, arg1 filters.Args) (types.ContainersPruneReport, error) {
return w.pimpl.ContainersPrune(arg0, arg1)
}
func (w withPodman) CopyFromContainer(arg0 context.Context, arg1 string, arg2 string) (io.ReadCloser, types.ContainerPathStat, error) {
return w.pimpl.CopyFromContainer(arg0, arg1, arg2)
}
func (w withPodman) CopyToContainer(arg0 context.Context, arg1 string, arg2 string, arg3 io.Reader, arg4 types.CopyToContainerOptions) error {
return w.pimpl.CopyToContainer(arg0, arg1, arg2, arg3, arg4)
}
func (w withPodman) DaemonHost() string {
return w.pimpl.DaemonHost()
}
func (w withPodman) DialHijack(arg0 context.Context, arg1 string, arg2 string, arg3 map[string][]string) (net.Conn, error) {
return w.pimpl.DialHijack(arg0, arg1, arg2, arg3)
}
func (w withPodman) Dialer() func(context.Context) (net.Conn, error) {
return w.pimpl.Dialer()
}
func (w withPodman) DiskUsage(arg0 context.Context) (types.DiskUsage, error) {
return w.pimpl.DiskUsage(arg0)
}
func (w withPodman) DistributionInspect(arg0 context.Context, arg1 string, arg2 string) (registry.DistributionInspect, error) {
return w.pimpl.DistributionInspect(arg0, arg1, arg2)
}
func (w withPodman) Events(arg0 context.Context, arg1 types.EventsOptions) (<-chan events.Message, <-chan error) {
return w.pimpl.Events(arg0, arg1)
}
func (w withPodman) HTTPClient() *http.Client {
return w.pimpl.HTTPClient()
}
func (w withPodman) ImageBuild(arg0 context.Context, arg1 io.Reader, arg2 types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return w.pimpl.ImageBuild(arg0, arg1, arg2)
}
func (w withPodman) ImageCreate(arg0 context.Context, arg1 string, arg2 types.ImageCreateOptions) (io.ReadCloser, error) {
return w.pimpl.ImageCreate(arg0, arg1, arg2)
}
func (w withPodman) ImageHistory(arg0 context.Context, arg1 string) ([]image.HistoryResponseItem, error) {
return w.pimpl.ImageHistory(arg0, arg1)
}
func (w withPodman) ImageImport(arg0 context.Context, arg1 types.ImageImportSource, arg2 string, arg3 types.ImageImportOptions) (io.ReadCloser, error) {
return w.pimpl.ImageImport(arg0, arg1, arg2, arg3)
}
func (w withPodman) ImageInspectWithRaw(arg0 context.Context, arg1 string) (types.ImageInspect, []uint8, error) {
return w.pimpl.ImageInspectWithRaw(arg0, arg1)
}
func (w withPodman) ImageList(arg0 context.Context, arg1 types.ImageListOptions) ([]types.ImageSummary, error) {
return w.pimpl.ImageList(arg0, arg1)
}
func (w withPodman) ImageLoad(arg0 context.Context, arg1 io.Reader, arg2 bool) (types.ImageLoadResponse, error) {
return w.pimpl.ImageLoad(arg0, arg1, arg2)
}
func (w withPodman) ImagePull(arg0 context.Context, arg1 string, arg2 types.ImagePullOptions) (io.ReadCloser, error) {
return w.pimpl.ImagePull(arg0, arg1, arg2)
}
func (w withPodman) ImagePush(arg0 context.Context, arg1 string, arg2 types.ImagePushOptions) (io.ReadCloser, error) {
return w.pimpl.ImagePush(arg0, arg1, arg2)
}
func (w withPodman) ImageRemove(arg0 context.Context, arg1 string, arg2 types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
return w.pimpl.ImageRemove(arg0, arg1, arg2)
}
func (w withPodman) ImageSave(arg0 context.Context, arg1 []string) (io.ReadCloser, error) {
return w.pimpl.ImageSave(arg0, arg1)
}
func (w withPodman) ImageSearch(arg0 context.Context, arg1 string, arg2 types.ImageSearchOptions) ([]registry.SearchResult, error) {
return w.pimpl.ImageSearch(arg0, arg1, arg2)
}
func (w withPodman) ImageTag(arg0 context.Context, arg1 string, arg2 string) error {
return w.pimpl.ImageTag(arg0, arg1, arg2)
}
func (w withPodman) ImagesPrune(arg0 context.Context, arg1 filters.Args) (types.ImagesPruneReport, error) {
return w.pimpl.ImagesPrune(arg0, arg1)
}
func (w withPodman) Info(arg0 context.Context) (types.Info, error) {
return w.pimpl.Info(arg0)
}
func (w withPodman) NegotiateAPIVersion(arg0 context.Context) {
w.pimpl.NegotiateAPIVersion(arg0)
}
func (w withPodman) NegotiateAPIVersionPing(arg0 types.Ping) {
w.pimpl.NegotiateAPIVersionPing(arg0)
}
func (w withPodman) NetworkConnect(arg0 context.Context, arg1 string, arg2 string, arg3 *network.EndpointSettings) error {
return w.pimpl.NetworkConnect(arg0, arg1, arg2, arg3)
}
func (w withPodman) NetworkCreate(arg0 context.Context, arg1 string, arg2 types.NetworkCreate) (types.NetworkCreateResponse, error) {
return w.pimpl.NetworkCreate(arg0, arg1, arg2)
}
func (w withPodman) NetworkDisconnect(arg0 context.Context, arg1 string, arg2 string, arg3 bool) error {
return w.pimpl.NetworkDisconnect(arg0, arg1, arg2, arg3)
}
func (w withPodman) NetworkInspect(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, error) {
return w.pimpl.NetworkInspect(arg0, arg1, arg2)
}
func (w withPodman) NetworkInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, []uint8, error) {
return w.pimpl.NetworkInspectWithRaw(arg0, arg1, arg2)
}
func (w withPodman) NetworkList(arg0 context.Context, arg1 types.NetworkListOptions) ([]types.NetworkResource, error) {
return w.pimpl.NetworkList(arg0, arg1)
}
func (w withPodman) NetworkRemove(arg0 context.Context, arg1 string) error {
return w.pimpl.NetworkRemove(arg0, arg1)
}
func (w withPodman) NetworksPrune(arg0 context.Context, arg1 filters.Args) (types.NetworksPruneReport, error) {
return w.pimpl.NetworksPrune(arg0, arg1)
}
func (w withPodman) NodeInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Node, []uint8, error) {
return w.pimpl.NodeInspectWithRaw(arg0, arg1)
}
func (w withPodman) NodeList(arg0 context.Context, arg1 types.NodeListOptions) ([]swarm.Node, error) {
return w.pimpl.NodeList(arg0, arg1)
}
func (w withPodman) NodeRemove(arg0 context.Context, arg1 string, arg2 types.NodeRemoveOptions) error {
return w.pimpl.NodeRemove(arg0, arg1, arg2)
}
func (w withPodman) NodeUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.NodeSpec) error {
return w.pimpl.NodeUpdate(arg0, arg1, arg2, arg3)
}
func (w withPodman) Ping(arg0 context.Context) (types.Ping, error) {
return w.pimpl.Ping(arg0)
}
func (w withPodman) PluginCreate(arg0 context.Context, arg1 io.Reader, arg2 types.PluginCreateOptions) error {
return w.pimpl.PluginCreate(arg0, arg1, arg2)
}
func (w withPodman) PluginDisable(arg0 context.Context, arg1 string, arg2 types.PluginDisableOptions) error {
return w.pimpl.PluginDisable(arg0, arg1, arg2)
}
func (w withPodman) PluginEnable(arg0 context.Context, arg1 string, arg2 types.PluginEnableOptions) error {
return w.pimpl.PluginEnable(arg0, arg1, arg2)
}
func (w withPodman) PluginInspectWithRaw(arg0 context.Context, arg1 string) (*types.Plugin, []uint8, error) {
return w.pimpl.PluginInspectWithRaw(arg0, arg1)
}
func (w withPodman) PluginInstall(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) {
return w.pimpl.PluginInstall(arg0, arg1, arg2)
}
func (w withPodman) PluginList(arg0 context.Context, arg1 filters.Args) (types.PluginsListResponse, error) {
return w.pimpl.PluginList(arg0, arg1)
}
func (w withPodman) PluginPush(arg0 context.Context, arg1 string, arg2 string) (io.ReadCloser, error) {
return w.pimpl.PluginPush(arg0, arg1, arg2)
}
func (w withPodman) PluginRemove(arg0 context.Context, arg1 string, arg2 types.PluginRemoveOptions) error {
return w.pimpl.PluginRemove(arg0, arg1, arg2)
}
func (w withPodman) PluginSet(arg0 context.Context, arg1 string, arg2 []string) error {
return w.pimpl.PluginSet(arg0, arg1, arg2)
}
func (w withPodman) PluginUpgrade(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) {
return w.pimpl.PluginUpgrade(arg0, arg1, arg2)
}
func (w withPodman) RegistryLogin(arg0 context.Context, arg1 types.AuthConfig) (registry.AuthenticateOKBody, error) {
return w.pimpl.RegistryLogin(arg0, arg1)
}
func (w withPodman) SecretCreate(arg0 context.Context, arg1 swarm.SecretSpec) (types.SecretCreateResponse, error) {
return w.pimpl.SecretCreate(arg0, arg1)
}
func (w withPodman) SecretInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Secret, []uint8, error) {
return w.pimpl.SecretInspectWithRaw(arg0, arg1)
}
func (w withPodman) SecretList(arg0 context.Context, arg1 types.SecretListOptions) ([]swarm.Secret, error) {
return w.pimpl.SecretList(arg0, arg1)
}
func (w withPodman) SecretRemove(arg0 context.Context, arg1 string) error {
return w.pimpl.SecretRemove(arg0, arg1)
}
func (w withPodman) SecretUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.SecretSpec) error {
return w.pimpl.SecretUpdate(arg0, arg1, arg2, arg3)
}
func (w withPodman) ServerVersion(arg0 context.Context) (types.Version, error) {
return w.pimpl.ServerVersion(arg0)
}
func (w withPodman) ServiceCreate(arg0 context.Context, arg1 swarm.ServiceSpec, arg2 types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
return w.pimpl.ServiceCreate(arg0, arg1, arg2)
}
func (w withPodman) ServiceInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.ServiceInspectOptions) (swarm.Service, []uint8, error) {
return w.pimpl.ServiceInspectWithRaw(arg0, arg1, arg2)
}
func (w withPodman) ServiceList(arg0 context.Context, arg1 types.ServiceListOptions) ([]swarm.Service, error) {
return w.pimpl.ServiceList(arg0, arg1)
}
func (w withPodman) ServiceLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
return w.pimpl.ServiceLogs(arg0, arg1, arg2)
}
func (w withPodman) ServiceRemove(arg0 context.Context, arg1 string) error {
return w.pimpl.ServiceRemove(arg0, arg1)
}
func (w withPodman) ServiceUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ServiceSpec, arg4 types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
return w.pimpl.ServiceUpdate(arg0, arg1, arg2, arg3, arg4)
}
func (w withPodman) SwarmGetUnlockKey(arg0 context.Context) (types.SwarmUnlockKeyResponse, error) {
return w.pimpl.SwarmGetUnlockKey(arg0)
}
func (w withPodman) SwarmInit(arg0 context.Context, arg1 swarm.InitRequest) (string, error) {
return w.pimpl.SwarmInit(arg0, arg1)
}
func (w withPodman) SwarmInspect(arg0 context.Context) (swarm.Swarm, error) {
return w.pimpl.SwarmInspect(arg0)
}
func (w withPodman) SwarmJoin(arg0 context.Context, arg1 swarm.JoinRequest) error {
return w.pimpl.SwarmJoin(arg0, arg1)
}
func (w withPodman) SwarmLeave(arg0 context.Context, arg1 bool) error {
return w.pimpl.SwarmLeave(arg0, arg1)
}
func (w withPodman) SwarmUnlock(arg0 context.Context, arg1 swarm.UnlockRequest) error {
return w.pimpl.SwarmUnlock(arg0, arg1)
}
func (w withPodman) SwarmUpdate(arg0 context.Context, arg1 swarm.Version, arg2 swarm.Spec, arg3 swarm.UpdateFlags) error {
return w.pimpl.SwarmUpdate(arg0, arg1, arg2, arg3)
}
func (w withPodman) TaskInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Task, []uint8, error) {
return w.pimpl.TaskInspectWithRaw(arg0, arg1)
}
func (w withPodman) TaskList(arg0 context.Context, arg1 types.TaskListOptions) ([]swarm.Task, error) {
return w.pimpl.TaskList(arg0, arg1)
}
func (w withPodman) TaskLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) {
return w.pimpl.TaskLogs(arg0, arg1, arg2)
}
func (w withPodman) VolumeCreate(arg0 context.Context, arg1 volume.VolumeCreateBody) (types.Volume, error) {
return w.pimpl.VolumeCreate(arg0, arg1)
}
func (w withPodman) VolumeInspect(arg0 context.Context, arg1 string) (types.Volume, error) {
return w.pimpl.VolumeInspect(arg0, arg1)
}
func (w withPodman) VolumeInspectWithRaw(arg0 context.Context, arg1 string) (types.Volume, []uint8, error) {
return w.pimpl.VolumeInspectWithRaw(arg0, arg1)
}
func (w withPodman) VolumeList(arg0 context.Context, arg1 filters.Args) (volume.VolumeListOKBody, error) {
return w.pimpl.VolumeList(arg0, arg1)
}
func (w withPodman) VolumeRemove(arg0 context.Context, arg1 string, arg2 bool) error {
return w.pimpl.VolumeRemove(arg0, arg1, arg2)
}
func (w withPodman) VolumesPrune(arg0 context.Context, arg1 filters.Args) (types.VolumesPruneReport, error) {
return w.pimpl.VolumesPrune(arg0, arg1)
}
//</editor-fold>

View File

@ -0,0 +1,114 @@
package docker_test
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"knative.dev/kn-plugin-func/docker"
)
// Test that we are starting podman service on behalf of user
// if docker daemon is not present.
func TestNewDockerClientWithAutomaticPodman(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
defer withMockedPodmanBinary(t)()
defer withEnvVar(t, "DOCKER_HOST", "")()
dockerClient, _, err := docker.NewClient("unix:///var/run/nonexistent.sock")
if err != nil {
t.Error(err)
}
defer dockerClient.Close()
_, err = dockerClient.Ping(ctx)
if err != nil {
t.Error(err)
}
}
const helperGoScriptContent = `package main
import (
"fmt"
"net"
"net/http"
"net/url"
"os"
)
func main() {
dockerHost := os.Args[3]
u, err := url.Parse(dockerHost)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
sock := u.Path
server := http.Server{}
listener, err := net.Listen("unix", sock)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// mimics /_ping endpoint
server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Add("Content-Type", "text/plain")
writer.WriteHeader(200)
writer.Write([]byte("OK"))
})
server.Serve(listener)
}
`
func withMockedPodmanBinary(t *testing.T) func() {
var err error
binDir := t.TempDir()
newPath := binDir + string(os.PathListSeparator) + os.Getenv("PATH")
cleanUpPath := withEnvVar(t, "PATH", newPath)
helperGoScriptPath := filepath.Join(binDir, "main.go")
err = ioutil.WriteFile(helperGoScriptPath,
[]byte(helperGoScriptContent),
0400)
if err != nil {
t.Fatal(err)
}
runnerScriptName := "podman"
if runtime.GOOS == "windows" {
runnerScriptName = runnerScriptName + ".bat"
}
runnerScriptContent := `#!/bin/sh
exec go run GO_SCRIPT_PATH $@;
`
if runtime.GOOS == "windows" {
runnerScriptContent = `@echo off
go.exe run GO_SCRIPT_PATH %*
`
}
runnerScriptPath := filepath.Join(binDir, runnerScriptName)
runnerScriptContent = strings.ReplaceAll(runnerScriptContent, "GO_SCRIPT_PATH", helperGoScriptPath)
err = ioutil.WriteFile(runnerScriptPath, []byte(runnerScriptContent), 0700)
if err != nil {
t.Fatal(err)
}
return func() {
cleanUpPath()
}
}

View File

@ -0,0 +1,86 @@
package docker_test
import (
"context"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/docker/docker/client"
"knative.dev/kn-plugin-func/docker"
)
// Test that we are creating client in accordance
// with the DOCKER_HOST environment variable
func TestNewDockerClient(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
tmpDir := t.TempDir()
sock := filepath.Join(tmpDir, "docker.sock")
defer startMockDaemon(t, sock)()
defer withEnvVar(t, "DOCKER_HOST", fmt.Sprintf("unix://%s", sock))()
dockerClient, _, err := docker.NewClient(client.DefaultDockerHost)
if err != nil {
t.Error(err)
}
defer dockerClient.Close()
_, err = dockerClient.Ping(ctx)
if err != nil {
t.Error(err)
}
}
func withEnvVar(t *testing.T, name, value string) func() {
oldDh, hadDh := os.LookupEnv(name)
err := os.Setenv(name, value)
if err != nil {
t.Fatal(err)
}
return func() {
if hadDh {
os.Setenv(name, oldDh)
} else {
os.Unsetenv(name)
}
}
}
func startMockDaemon(t *testing.T, sock string) func() {
server := http.Server{}
listener, err := net.Listen("unix", sock)
if err != nil {
t.Fatal(err)
}
// mimics /_ping endpoint
server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Add("Content-Type", "text/plain")
writer.WriteHeader(200)
_, _ = writer.Write([]byte("OK"))
})
serErrChan := make(chan error)
go func() {
serErrChan <- server.Serve(listener)
}()
return func() {
err := server.Shutdown(context.Background())
if err != nil {
t.Fatal(err)
}
err = <-serErrChan
if err != nil && !strings.Contains(err.Error(), "Server closed") {
t.Fatal(err)
}
}
}

View File

@ -13,6 +13,8 @@ import (
"regexp"
"strings"
"github.com/docker/docker/client"
fn "knative.dev/kn-plugin-func"
"github.com/containers/image/v5/pkg/docker/config"
@ -39,10 +41,11 @@ var ErrUnauthorized = errors.New("bad credentials")
type VerifyCredentialsCallback func(ctx context.Context, username, password, registry string) error
func CheckAuth(ctx context.Context, username, password, registry string) error {
cli, _, err := NewDockerClient()
cli, _, err := NewClient(client.DefaultDockerHost)
if err != nil {
return err
}
defer cli.Close()
_, err = cli.RegistryLogin(ctx, types.AuthConfig{Username: username, Password: password, ServerAddress: registry})
if err != nil && strings.Contains(err.Error(), "401 Unauthorized") {
@ -242,10 +245,11 @@ func (n *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err er
return "", err
}
cli, _, err := NewDockerClient()
cli, _, err := NewClient(client.DefaultDockerHost)
if err != nil {
return "", fmt.Errorf("failed to create docker api client: %w", err)
}
defer cli.Close()
n.progressListener.Stopping()
credentials, err := n.credentialsProvider(ctx, registry)

View File

@ -8,6 +8,8 @@ import (
"strings"
"time"
"github.com/docker/docker/client"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
@ -33,10 +35,11 @@ func (n *Runner) Run(ctx context.Context, f fn.Function) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
cli, _, err := NewDockerClient()
cli, _, err := NewClient(client.DefaultDockerHost)
if err != nil {
return errors.Wrap(err, "failed to create docker api client")
}
defer cli.Close()
if f.Image == "" {
return errors.New("Function has no associated Image. Has it been built?")

1
go.mod
View File

@ -21,6 +21,7 @@ require (
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c
github.com/markbates/pkger v0.17.1
github.com/mitchellh/go-homedir v1.1.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/ory/viper v1.7.5
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.2.1

1
vendor/modules.txt vendored
View File

@ -536,6 +536,7 @@ github.com/morikuni/aec
# github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/go-digest
# github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
## explicit
github.com/opencontainers/image-spec/specs-go
github.com/opencontainers/image-spec/specs-go/v1
# github.com/opencontainers/runc v1.0.0-rc93