2014-07-15 15:34:48 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
package dockertools
|
2014-07-15 15:34:48 +00:00
|
|
|
|
|
|
|
import (
|
2014-10-14 10:08:48 +00:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2014-07-18 14:54:43 +00:00
|
|
|
"errors"
|
2014-07-15 15:34:48 +00:00
|
|
|
"fmt"
|
2014-08-07 23:59:18 +00:00
|
|
|
"hash/adler32"
|
2014-09-24 21:27:10 +00:00
|
|
|
"io"
|
2014-11-07 06:41:16 +00:00
|
|
|
"io/ioutil"
|
2014-07-15 15:34:48 +00:00
|
|
|
"math/rand"
|
2015-02-05 00:58:26 +00:00
|
|
|
"os"
|
2014-08-07 18:15:11 +00:00
|
|
|
"os/exec"
|
2014-08-07 23:59:18 +00:00
|
|
|
"strconv"
|
2014-07-15 15:34:48 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-11-15 13:50:59 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/credentialprovider"
|
2015-01-28 18:13:02 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/leaky"
|
2015-01-14 23:22:21 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
2014-09-26 04:24:44 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
2014-11-15 13:50:59 +00:00
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
2014-08-07 23:59:18 +00:00
|
|
|
"github.com/golang/glog"
|
2014-07-15 15:34:48 +00:00
|
|
|
)
|
|
|
|
|
2015-01-21 00:59:26 +00:00
|
|
|
const (
|
2015-01-28 18:13:02 +00:00
|
|
|
PodInfraContainerName = leaky.PodInfraContainerName
|
2015-02-05 22:24:48 +00:00
|
|
|
DockerPrefix = "docker://"
|
2015-01-21 00:59:26 +00:00
|
|
|
)
|
|
|
|
|
2014-07-15 15:34:48 +00:00
|
|
|
// DockerInterface is an abstract interface for testability. It abstracts the interface of docker.Client.
|
|
|
|
type DockerInterface interface {
|
|
|
|
ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error)
|
|
|
|
InspectContainer(id string) (*docker.Container, error)
|
|
|
|
CreateContainer(docker.CreateContainerOptions) (*docker.Container, error)
|
|
|
|
StartContainer(id string, hostConfig *docker.HostConfig) error
|
|
|
|
StopContainer(id string, timeout uint) error
|
2014-10-28 00:29:55 +00:00
|
|
|
RemoveContainer(opts docker.RemoveContainerOptions) error
|
2014-09-26 04:53:17 +00:00
|
|
|
InspectImage(image string) (*docker.Image, error)
|
2014-12-22 19:54:07 +00:00
|
|
|
ListImages(opts docker.ListImagesOptions) ([]docker.APIImages, error)
|
2014-07-15 15:34:48 +00:00
|
|
|
PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
|
2014-12-22 19:54:07 +00:00
|
|
|
RemoveImage(image string) error
|
2014-08-27 19:41:32 +00:00
|
|
|
Logs(opts docker.LogsOptions) error
|
2014-10-14 10:08:48 +00:00
|
|
|
Version() (*docker.Env, error)
|
|
|
|
CreateExec(docker.CreateExecOptions) (*docker.Exec, error)
|
|
|
|
StartExec(string, docker.StartExecOptions) error
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DockerID is an ID of docker container. It is a type to make it clear when we're working with docker container Ids
|
|
|
|
type DockerID string
|
|
|
|
|
|
|
|
// DockerPuller is an abstract interface for testability. It abstracts image pull operations.
|
|
|
|
type DockerPuller interface {
|
|
|
|
Pull(image string) error
|
2014-09-26 04:53:17 +00:00
|
|
|
IsImagePresent(image string) (bool, error)
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// dockerPuller is the default implementation of DockerPuller.
|
|
|
|
type dockerPuller struct {
|
2014-09-06 01:13:19 +00:00
|
|
|
client DockerInterface
|
2014-11-15 13:50:59 +00:00
|
|
|
keyring credentialprovider.DockerKeyring
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
2014-09-26 04:24:44 +00:00
|
|
|
type throttledDockerPuller struct {
|
|
|
|
puller dockerPuller
|
|
|
|
limiter util.RateLimiter
|
|
|
|
}
|
|
|
|
|
2014-07-15 15:34:48 +00:00
|
|
|
// NewDockerPuller creates a new instance of the default implementation of DockerPuller.
|
2014-09-26 04:24:44 +00:00
|
|
|
func NewDockerPuller(client DockerInterface, qps float32, burst int) DockerPuller {
|
2014-09-06 01:13:19 +00:00
|
|
|
dp := dockerPuller{
|
|
|
|
client: client,
|
2014-11-15 13:50:59 +00:00
|
|
|
keyring: credentialprovider.NewDockerKeyring(),
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
2014-09-06 01:13:19 +00:00
|
|
|
|
2014-09-26 04:24:44 +00:00
|
|
|
if qps == 0.0 {
|
|
|
|
return dp
|
|
|
|
}
|
|
|
|
return &throttledDockerPuller{
|
|
|
|
puller: dp,
|
|
|
|
limiter: util.NewTokenBucketRateLimiter(qps, burst),
|
|
|
|
}
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
2014-10-14 10:08:48 +00:00
|
|
|
type dockerContainerCommandRunner struct {
|
|
|
|
client DockerInterface
|
|
|
|
}
|
|
|
|
|
2014-12-16 21:06:58 +00:00
|
|
|
// The first version of docker that supports exec natively is 1.3.0 == API 1.15
|
|
|
|
var dockerAPIVersionWithExec = []uint{1, 15}
|
2014-10-14 10:08:48 +00:00
|
|
|
|
|
|
|
// Returns the major and minor version numbers of docker server.
|
2015-02-04 19:50:21 +00:00
|
|
|
func (d *dockerContainerCommandRunner) GetDockerServerVersion() ([]uint, error) {
|
2014-10-14 10:08:48 +00:00
|
|
|
env, err := d.client.Version()
|
|
|
|
if err != nil {
|
2014-11-20 10:00:36 +00:00
|
|
|
return nil, fmt.Errorf("failed to get docker server version - %v", err)
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
|
|
|
version := []uint{}
|
|
|
|
for _, entry := range *env {
|
2014-12-16 21:06:58 +00:00
|
|
|
if strings.Contains(strings.ToLower(entry), "apiversion") || strings.Contains(strings.ToLower(entry), "api version") {
|
2014-10-14 10:08:48 +00:00
|
|
|
elems := strings.Split(strings.Split(entry, "=")[1], ".")
|
|
|
|
for _, elem := range elems {
|
|
|
|
val, err := strconv.ParseUint(elem, 10, 32)
|
|
|
|
if err != nil {
|
2014-11-20 10:00:36 +00:00
|
|
|
return nil, fmt.Errorf("failed to parse docker server version %q: %v", entry, err)
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
|
|
|
version = append(version, uint(val))
|
|
|
|
}
|
|
|
|
return version, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("docker server version missing from server version output - %+v", env)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dockerContainerCommandRunner) nativeExecSupportExists() (bool, error) {
|
2015-02-04 19:50:21 +00:00
|
|
|
version, err := d.GetDockerServerVersion()
|
2014-10-14 10:08:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2014-12-16 21:06:58 +00:00
|
|
|
if len(dockerAPIVersionWithExec) != len(version) {
|
|
|
|
return false, fmt.Errorf("unexpected docker version format. Expecting %v format, got %v", dockerAPIVersionWithExec, version)
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
2014-12-16 21:06:58 +00:00
|
|
|
for idx, val := range dockerAPIVersionWithExec {
|
2014-10-14 10:08:48 +00:00
|
|
|
if version[idx] < val {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
2014-08-07 18:15:11 +00:00
|
|
|
|
|
|
|
func (d *dockerContainerCommandRunner) getRunInContainerCommand(containerID string, cmd []string) (*exec.Cmd, error) {
|
|
|
|
args := append([]string{"exec"}, cmd...)
|
|
|
|
command := exec.Command("/usr/sbin/nsinit", args...)
|
|
|
|
command.Dir = fmt.Sprintf("/var/lib/docker/execdriver/native/%s", containerID)
|
|
|
|
return command, nil
|
|
|
|
}
|
|
|
|
|
2014-10-14 10:08:48 +00:00
|
|
|
func (d *dockerContainerCommandRunner) runInContainerUsingNsinit(containerID string, cmd []string) ([]byte, error) {
|
2014-08-07 18:15:11 +00:00
|
|
|
c, err := d.getRunInContainerCommand(containerID, cmd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return c.CombinedOutput()
|
|
|
|
}
|
|
|
|
|
2014-10-14 10:08:48 +00:00
|
|
|
// RunInContainer uses nsinit to run the command inside the container identified by containerID
|
|
|
|
func (d *dockerContainerCommandRunner) RunInContainer(containerID string, cmd []string) ([]byte, error) {
|
|
|
|
// If native exec support does not exist in the local docker daemon use nsinit.
|
|
|
|
useNativeExec, err := d.nativeExecSupportExists()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !useNativeExec {
|
|
|
|
return d.runInContainerUsingNsinit(containerID, cmd)
|
|
|
|
}
|
|
|
|
createOpts := docker.CreateExecOptions{
|
|
|
|
Container: containerID,
|
|
|
|
Cmd: cmd,
|
|
|
|
AttachStdin: false,
|
|
|
|
AttachStdout: true,
|
|
|
|
AttachStderr: true,
|
|
|
|
Tty: false,
|
|
|
|
}
|
|
|
|
execObj, err := d.client.CreateExec(createOpts)
|
|
|
|
if err != nil {
|
2014-11-20 10:00:36 +00:00
|
|
|
return nil, fmt.Errorf("failed to run in container - Exec setup failed - %v", err)
|
2014-10-14 10:08:48 +00:00
|
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
wrBuf := bufio.NewWriter(&buf)
|
|
|
|
startOpts := docker.StartExecOptions{
|
|
|
|
Detach: false,
|
|
|
|
Tty: false,
|
|
|
|
OutputStream: wrBuf,
|
|
|
|
ErrorStream: wrBuf,
|
|
|
|
RawTerminal: false,
|
|
|
|
}
|
|
|
|
errChan := make(chan error, 1)
|
|
|
|
go func() {
|
2014-12-18 19:15:35 +00:00
|
|
|
errChan <- d.client.StartExec(execObj.ID, startOpts)
|
2014-10-14 10:08:48 +00:00
|
|
|
}()
|
|
|
|
wrBuf.Flush()
|
|
|
|
return buf.Bytes(), <-errChan
|
|
|
|
}
|
|
|
|
|
2015-01-08 20:41:38 +00:00
|
|
|
// ExecInContainer uses nsenter to run the command inside the container identified by containerID.
|
|
|
|
//
|
|
|
|
// TODO:
|
|
|
|
// - match cgroups of container
|
|
|
|
// - should we support `docker exec`?
|
|
|
|
// - should we support nsenter in a container, running with elevated privs and --pid=host?
|
|
|
|
func (d *dockerContainerCommandRunner) ExecInContainer(containerId string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error {
|
|
|
|
container, err := d.client.InspectContainer(containerId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !container.State.Running {
|
|
|
|
return fmt.Errorf("container not running (%s)", container)
|
|
|
|
}
|
|
|
|
|
|
|
|
containerPid := container.State.Pid
|
|
|
|
|
|
|
|
// TODO what if the container doesn't have `env`???
|
|
|
|
args := []string{"-t", fmt.Sprintf("%d", containerPid), "-m", "-i", "-u", "-n", "-p", "--", "env", "-i"}
|
|
|
|
args = append(args, fmt.Sprintf("HOSTNAME=%s", container.Config.Hostname))
|
|
|
|
args = append(args, container.Config.Env...)
|
|
|
|
args = append(args, cmd...)
|
|
|
|
command := exec.Command("nsenter", args...)
|
|
|
|
// TODO use exec.LookPath
|
|
|
|
if tty {
|
|
|
|
p, err := StartPty(command)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer p.Close()
|
|
|
|
|
|
|
|
// make sure to close the stdout stream
|
|
|
|
defer stdout.Close()
|
|
|
|
|
|
|
|
if stdin != nil {
|
|
|
|
go io.Copy(p, stdin)
|
|
|
|
}
|
|
|
|
|
|
|
|
if stdout != nil {
|
|
|
|
go io.Copy(stdout, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
return command.Wait()
|
|
|
|
} else {
|
|
|
|
cp := func(dst io.WriteCloser, src io.Reader, closeDst bool) {
|
|
|
|
defer func() {
|
|
|
|
if closeDst {
|
|
|
|
dst.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
if stdin != nil {
|
|
|
|
inPipe, err := command.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
cp(inPipe, stdin, false)
|
|
|
|
inPipe.Close()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
if stdout != nil {
|
|
|
|
outPipe, err := command.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go cp(stdout, outPipe, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
if stderr != nil {
|
|
|
|
errPipe, err := command.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go cp(stderr, errPipe, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return command.Run()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PortForward executes socat in the pod's network namespace and copies
|
|
|
|
// data between stream (representing the user's local connection on their
|
|
|
|
// computer) and the specified port in the container.
|
|
|
|
//
|
|
|
|
// TODO:
|
|
|
|
// - match cgroups of container
|
|
|
|
// - should we support nsenter + socat on the host? (current impl)
|
|
|
|
// - should we support nsenter + socat in a container, running with elevated privs and --pid=host?
|
|
|
|
func (d *dockerContainerCommandRunner) PortForward(podInfraContainerID string, port uint16, stream io.ReadWriteCloser) error {
|
|
|
|
container, err := d.client.InspectContainer(podInfraContainerID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !container.State.Running {
|
|
|
|
return fmt.Errorf("container not running (%s)", container)
|
|
|
|
}
|
|
|
|
|
|
|
|
containerPid := container.State.Pid
|
|
|
|
// TODO use exec.LookPath for socat / what if the host doesn't have it???
|
|
|
|
args := []string{"-t", fmt.Sprintf("%d", containerPid), "-n", "socat", "-", fmt.Sprintf("TCP4:localhost:%d", port)}
|
|
|
|
// TODO use exec.LookPath
|
|
|
|
command := exec.Command("nsenter", args...)
|
|
|
|
in, err := command.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out, err := command.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go io.Copy(in, stream)
|
|
|
|
go io.Copy(stream, out)
|
|
|
|
return command.Run()
|
|
|
|
}
|
|
|
|
|
2014-08-07 18:15:11 +00:00
|
|
|
// NewDockerContainerCommandRunner creates a ContainerCommandRunner which uses nsinit to run a command
|
|
|
|
// inside a container.
|
2014-11-05 00:58:37 +00:00
|
|
|
func NewDockerContainerCommandRunner(client DockerInterface) ContainerCommandRunner {
|
|
|
|
return &dockerContainerCommandRunner{client: client}
|
2014-08-07 18:15:11 +00:00
|
|
|
}
|
|
|
|
|
2014-07-15 15:34:48 +00:00
|
|
|
func (p dockerPuller) Pull(image string) error {
|
|
|
|
image, tag := parseImageName(image)
|
2014-07-20 17:31:26 +00:00
|
|
|
|
|
|
|
// If no tag was specified, use the default "latest".
|
|
|
|
if len(tag) == 0 {
|
|
|
|
tag = "latest"
|
|
|
|
}
|
|
|
|
|
2014-07-15 15:34:48 +00:00
|
|
|
opts := docker.PullImageOptions{
|
|
|
|
Repository: image,
|
|
|
|
Tag: tag,
|
|
|
|
}
|
2014-09-06 01:13:19 +00:00
|
|
|
|
2014-11-15 13:50:59 +00:00
|
|
|
creds, ok := p.keyring.Lookup(image)
|
2014-09-06 01:13:19 +00:00
|
|
|
if !ok {
|
|
|
|
glog.V(1).Infof("Pulling image %s without credentials", image)
|
|
|
|
}
|
|
|
|
|
2015-02-10 15:23:32 +00:00
|
|
|
err := p.client.PullImage(opts, creds)
|
|
|
|
// If there was no error, or we had credentials, just return the error.
|
|
|
|
if err == nil || ok {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Image spec: [<registry>/]<repository>/<image>[:<version] so we count '/'
|
|
|
|
explicitRegistry := (strings.Count(image, "/") == 2)
|
|
|
|
glog.Errorf("Foo: %s", explicitRegistry)
|
|
|
|
// Hack, look for a private registry, and decorate the error with the lack of
|
|
|
|
// credentials. This is heuristic, and really probably could be done better
|
|
|
|
// by talking to the registry API directly from the kubelet here.
|
|
|
|
if explicitRegistry {
|
|
|
|
return fmt.Errorf("image pull failed for %s, this may be because there are no credentials on this request. details: (%v)", image, err)
|
|
|
|
}
|
|
|
|
return err
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
2014-09-26 04:24:44 +00:00
|
|
|
func (p throttledDockerPuller) Pull(image string) error {
|
|
|
|
if p.limiter.CanAccept() {
|
|
|
|
return p.puller.Pull(image)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("pull QPS exceeded.")
|
|
|
|
}
|
|
|
|
|
2014-12-09 20:41:08 +00:00
|
|
|
func (p dockerPuller) IsImagePresent(image string) (bool, error) {
|
2014-09-26 04:53:17 +00:00
|
|
|
_, err := p.client.InspectImage(image)
|
|
|
|
if err == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
// This is super brittle, but its the best we got.
|
|
|
|
// TODO: Land code in the docker client to use docker.Error here instead.
|
|
|
|
if err.Error() == "no such image" {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2014-11-14 22:20:29 +00:00
|
|
|
// RequireLatestImage returns if the user wants the latest image
|
|
|
|
func RequireLatestImage(name string) bool {
|
|
|
|
_, tag := parseImageName(name)
|
|
|
|
|
|
|
|
if tag == "latest" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2014-09-26 04:53:17 +00:00
|
|
|
func (p throttledDockerPuller) IsImagePresent(name string) (bool, error) {
|
|
|
|
return p.puller.IsImagePresent(name)
|
|
|
|
}
|
|
|
|
|
2014-07-15 17:26:56 +00:00
|
|
|
// DockerContainers is a map of containers
|
|
|
|
type DockerContainers map[DockerID]*docker.APIContainers
|
|
|
|
|
2015-01-14 23:22:21 +00:00
|
|
|
func (c DockerContainers) FindPodContainer(podFullName string, uid types.UID, containerName string) (*docker.APIContainers, bool, uint64) {
|
2014-07-15 17:26:56 +00:00
|
|
|
for _, dockerContainer := range c {
|
2014-12-05 20:48:25 +00:00
|
|
|
if len(dockerContainer.Names) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2014-10-09 05:31:59 +00:00
|
|
|
// TODO(proppy): build the docker container name and do a map lookup instead?
|
2014-09-09 04:33:17 +00:00
|
|
|
dockerManifestID, dockerUUID, dockerContainerName, hash := ParseDockerName(dockerContainer.Names[0])
|
2014-09-05 09:49:11 +00:00
|
|
|
if dockerManifestID == podFullName &&
|
2015-01-14 21:53:43 +00:00
|
|
|
(uid == "" || dockerUUID == uid) &&
|
2014-09-05 09:49:11 +00:00
|
|
|
dockerContainerName == containerName {
|
2014-08-07 23:59:18 +00:00
|
|
|
return dockerContainer, true, hash
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-07 23:59:18 +00:00
|
|
|
return nil, false, 0
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 01:33:48 +00:00
|
|
|
// RemoveContainerWithID removes the container with the given containerID.
|
|
|
|
func (c DockerContainers) RemoveContainerWithID(containerID DockerID) {
|
|
|
|
delete(c, containerID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindContainersByPod returns the containers that belong to the pod.
|
|
|
|
func (c DockerContainers) FindContainersByPod(podUID types.UID, podFullName string) DockerContainers {
|
|
|
|
containers := make(DockerContainers)
|
2014-07-15 17:26:56 +00:00
|
|
|
|
|
|
|
for _, dockerContainer := range c {
|
2014-12-05 20:48:25 +00:00
|
|
|
if len(dockerContainer.Names) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2015-03-04 01:33:48 +00:00
|
|
|
dockerPodName, uuid, _, _ := ParseDockerName(dockerContainer.Names[0])
|
|
|
|
if podUID == uuid ||
|
|
|
|
(podUID == "" && podFullName == dockerPodName) {
|
|
|
|
containers[DockerID(dockerContainer.ID)] = dockerContainer
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return containers
|
|
|
|
}
|
|
|
|
|
2014-09-29 21:38:31 +00:00
|
|
|
// GetKubeletDockerContainers takes client and boolean whether to list all container or just the running ones.
|
|
|
|
// Returns a map of docker containers that we manage. The map key is the docker container ID
|
|
|
|
func GetKubeletDockerContainers(client DockerInterface, allContainers bool) (DockerContainers, error) {
|
2014-07-15 17:26:56 +00:00
|
|
|
result := make(DockerContainers)
|
2014-09-29 21:38:31 +00:00
|
|
|
containers, err := client.ListContainers(docker.ListContainersOptions{All: allContainers})
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for i := range containers {
|
|
|
|
container := &containers[i]
|
2014-12-05 20:48:25 +00:00
|
|
|
if len(container.Names) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2014-07-15 17:26:56 +00:00
|
|
|
// Skip containers that we didn't create to allow users to manually
|
|
|
|
// spin up their own containers if they want.
|
2014-09-25 00:05:53 +00:00
|
|
|
// TODO(dchen1107): Remove the old separator "--" by end of Oct
|
|
|
|
if !strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"_") &&
|
|
|
|
!strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"--") {
|
2014-11-03 16:38:02 +00:00
|
|
|
glog.V(3).Infof("Docker Container: %s is not managed by kubelet.", container.Names[0])
|
2014-07-15 17:26:56 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
result[DockerID(container.ID)] = container
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
// GetRecentDockerContainersWithNameAndUUID returns a list of dead docker containers which matches the name
|
2015-01-14 21:53:43 +00:00
|
|
|
// and uid given.
|
2015-01-14 23:22:21 +00:00
|
|
|
func GetRecentDockerContainersWithNameAndUUID(client DockerInterface, podFullName string, uid types.UID, containerName string) ([]*docker.Container, error) {
|
2014-08-26 18:25:17 +00:00
|
|
|
var result []*docker.Container
|
|
|
|
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, dockerContainer := range containers {
|
2014-11-21 02:28:23 +00:00
|
|
|
if len(dockerContainer.Names) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2014-09-09 04:33:17 +00:00
|
|
|
dockerPodName, dockerUUID, dockerContainerName, _ := ParseDockerName(dockerContainer.Names[0])
|
2014-08-26 18:25:17 +00:00
|
|
|
if dockerPodName != podFullName {
|
|
|
|
continue
|
|
|
|
}
|
2015-01-14 21:53:43 +00:00
|
|
|
if uid != "" && dockerUUID != uid {
|
2014-08-26 18:25:17 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if dockerContainerName != containerName {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
inspectResult, _ := client.InspectContainer(dockerContainer.ID)
|
|
|
|
if inspectResult != nil && !inspectResult.State.Running && !inspectResult.State.Paused {
|
|
|
|
result = append(result, inspectResult)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2014-08-27 19:41:32 +00:00
|
|
|
// GetKubeletDockerContainerLogs returns logs of specific container
|
2014-09-15 16:20:01 +00:00
|
|
|
// By default the function will return snapshot of the container log
|
|
|
|
// Log streaming is possible if 'follow' param is set to true
|
|
|
|
// Log tailing is possible when number of tailed lines are set and only if 'follow' is false
|
2014-11-12 10:42:55 +00:00
|
|
|
// TODO: Make 'RawTerminal' option flagable.
|
2014-09-22 20:14:23 +00:00
|
|
|
func GetKubeletDockerContainerLogs(client DockerInterface, containerID, tail string, follow bool, stdout, stderr io.Writer) (err error) {
|
2014-08-27 19:41:32 +00:00
|
|
|
opts := docker.LogsOptions{
|
|
|
|
Container: containerID,
|
|
|
|
Stdout: true,
|
|
|
|
Stderr: true,
|
2014-09-22 20:14:23 +00:00
|
|
|
OutputStream: stdout,
|
|
|
|
ErrorStream: stderr,
|
2014-08-27 19:41:32 +00:00
|
|
|
Timestamps: true,
|
2014-11-12 10:42:55 +00:00
|
|
|
RawTerminal: false,
|
2014-09-15 16:20:01 +00:00
|
|
|
Follow: follow,
|
2014-08-27 19:41:32 +00:00
|
|
|
}
|
|
|
|
|
2014-09-15 16:20:01 +00:00
|
|
|
if !follow {
|
2014-08-27 19:41:32 +00:00
|
|
|
opts.Tail = tail
|
|
|
|
}
|
|
|
|
|
|
|
|
err = client.Logs(opts)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-10-03 06:39:02 +00:00
|
|
|
var (
|
|
|
|
// ErrNoContainersInPod is returned when there are no containers for a given pod
|
|
|
|
ErrNoContainersInPod = errors.New("no containers exist for this pod")
|
|
|
|
|
2015-01-21 00:59:26 +00:00
|
|
|
// ErrNoPodInfraContainerInPod is returned when there is no pod infra container for a given pod
|
|
|
|
ErrNoPodInfraContainerInPod = errors.New("No pod infra container exists for this pod")
|
2014-10-03 06:39:02 +00:00
|
|
|
|
|
|
|
// ErrContainerCannotRun is returned when a container is created, but cannot run properly
|
|
|
|
ErrContainerCannotRun = errors.New("Container cannot run")
|
|
|
|
)
|
|
|
|
|
2014-11-07 06:41:16 +00:00
|
|
|
func inspectContainer(client DockerInterface, dockerID, containerName, tPath string) (*api.ContainerStatus, error) {
|
2014-10-03 06:39:02 +00:00
|
|
|
inspectResult, err := client.InspectContainer(dockerID)
|
2014-11-07 06:41:16 +00:00
|
|
|
|
2014-10-03 06:39:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-16 20:21:04 +00:00
|
|
|
if inspectResult == nil {
|
|
|
|
// Why did we not get an error?
|
2014-10-03 06:39:02 +00:00
|
|
|
return &api.ContainerStatus{}, nil
|
2014-09-16 20:21:04 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 19:59:01 +00:00
|
|
|
glog.V(3).Infof("Container inspect result: %+v", *inspectResult)
|
2014-10-06 18:54:51 +00:00
|
|
|
containerStatus := api.ContainerStatus{
|
2014-12-03 08:31:42 +00:00
|
|
|
Image: inspectResult.Config.Image,
|
2015-02-05 22:24:48 +00:00
|
|
|
ImageID: DockerPrefix + inspectResult.Image,
|
|
|
|
ContainerID: DockerPrefix + dockerID,
|
2014-10-06 18:54:51 +00:00
|
|
|
}
|
2014-09-16 20:21:04 +00:00
|
|
|
|
2014-10-03 23:25:54 +00:00
|
|
|
waiting := true
|
2014-09-16 20:21:04 +00:00
|
|
|
if inspectResult.State.Running {
|
2014-10-03 06:39:02 +00:00
|
|
|
containerStatus.State.Running = &api.ContainerStateRunning{
|
2014-12-10 22:00:41 +00:00
|
|
|
StartedAt: util.NewTime(inspectResult.State.StartedAt),
|
2014-10-03 06:39:02 +00:00
|
|
|
}
|
2015-01-21 00:59:26 +00:00
|
|
|
if containerName == PodInfraContainerName && inspectResult.NetworkSettings != nil {
|
2014-10-03 06:39:02 +00:00
|
|
|
containerStatus.PodIP = inspectResult.NetworkSettings.IPAddress
|
|
|
|
}
|
|
|
|
waiting = false
|
|
|
|
} else if !inspectResult.State.FinishedAt.IsZero() {
|
2014-12-16 23:29:55 +00:00
|
|
|
reason := ""
|
|
|
|
// Note: An application might handle OOMKilled gracefully.
|
|
|
|
// In that case, the container is oom killed, but the exit
|
|
|
|
// code could be 0.
|
|
|
|
if inspectResult.State.OOMKilled {
|
|
|
|
reason = "OOM Killed"
|
|
|
|
} else {
|
|
|
|
reason = inspectResult.State.Error
|
|
|
|
}
|
2014-09-16 20:21:04 +00:00
|
|
|
containerStatus.State.Termination = &api.ContainerStateTerminated{
|
2014-10-03 06:39:02 +00:00
|
|
|
ExitCode: inspectResult.State.ExitCode,
|
2014-12-16 23:29:55 +00:00
|
|
|
Reason: reason,
|
2014-12-10 22:00:41 +00:00
|
|
|
StartedAt: util.NewTime(inspectResult.State.StartedAt),
|
|
|
|
FinishedAt: util.NewTime(inspectResult.State.FinishedAt),
|
2014-09-16 20:21:04 +00:00
|
|
|
}
|
2014-11-07 06:41:16 +00:00
|
|
|
if tPath != "" {
|
|
|
|
path, found := inspectResult.Volumes[tPath]
|
|
|
|
if found {
|
|
|
|
data, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
2014-11-20 10:00:36 +00:00
|
|
|
glog.Errorf("Error on reading termination-log %s: %v", path, err)
|
2014-11-07 06:41:16 +00:00
|
|
|
} else {
|
|
|
|
containerStatus.State.Termination.Message = string(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-03 06:39:02 +00:00
|
|
|
waiting = false
|
2014-09-16 20:21:04 +00:00
|
|
|
}
|
|
|
|
|
2014-10-03 06:39:02 +00:00
|
|
|
if waiting {
|
|
|
|
// TODO(dchen1107): Separate issue docker/docker#8294 was filed
|
|
|
|
// TODO(dchen1107): Need to figure out why we are still waiting
|
|
|
|
// Check any issue to run container
|
|
|
|
containerStatus.State.Waiting = &api.ContainerStateWaiting{
|
|
|
|
Reason: ErrContainerCannotRun.Error(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &containerStatus, nil
|
|
|
|
}
|
2014-07-18 14:54:43 +00:00
|
|
|
|
2015-02-27 02:02:04 +00:00
|
|
|
// GetDockerPodInfo returns docker info for all containers in the pod/manifest and
|
|
|
|
// infrastructure container
|
2015-01-14 23:22:21 +00:00
|
|
|
func GetDockerPodInfo(client DockerInterface, manifest api.PodSpec, podFullName string, uid types.UID) (api.PodInfo, error) {
|
2014-07-15 17:26:56 +00:00
|
|
|
info := api.PodInfo{}
|
2014-11-07 06:41:16 +00:00
|
|
|
expectedContainers := make(map[string]api.Container)
|
|
|
|
for _, container := range manifest.Containers {
|
|
|
|
expectedContainers[container.Name] = container
|
|
|
|
}
|
2015-01-21 00:59:26 +00:00
|
|
|
expectedContainers[PodInfraContainerName] = api.Container{}
|
2014-07-15 17:26:56 +00:00
|
|
|
|
2014-09-10 20:20:29 +00:00
|
|
|
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, value := range containers {
|
2014-12-05 20:48:25 +00:00
|
|
|
if len(value.Names) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2014-09-09 04:33:17 +00:00
|
|
|
dockerManifestID, dockerUUID, dockerContainerName, _ := ParseDockerName(value.Names[0])
|
2014-07-18 18:42:47 +00:00
|
|
|
if dockerManifestID != podFullName {
|
2014-07-15 17:26:56 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-01-14 21:53:43 +00:00
|
|
|
if uid != "" && dockerUUID != uid {
|
2014-09-05 09:49:11 +00:00
|
|
|
continue
|
|
|
|
}
|
2014-11-07 06:41:16 +00:00
|
|
|
c, found := expectedContainers[dockerContainerName]
|
|
|
|
terminationMessagePath := ""
|
|
|
|
if !found {
|
|
|
|
// TODO(dchen1107): should figure out why not continue here
|
|
|
|
// continue
|
|
|
|
} else {
|
|
|
|
terminationMessagePath = c.TerminationMessagePath
|
|
|
|
}
|
2014-09-10 20:20:29 +00:00
|
|
|
// We assume docker return us a list of containers in time order
|
2014-09-16 20:21:04 +00:00
|
|
|
if containerStatus, found := info[dockerContainerName]; found {
|
|
|
|
containerStatus.RestartCount += 1
|
|
|
|
info[dockerContainerName] = containerStatus
|
2014-09-10 20:20:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-11-07 06:41:16 +00:00
|
|
|
containerStatus, err := inspectContainer(client, value.ID, dockerContainerName, terminationMessagePath)
|
2014-07-15 17:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-10-03 06:39:02 +00:00
|
|
|
info[dockerContainerName] = *containerStatus
|
2014-07-15 17:26:56 +00:00
|
|
|
}
|
2014-10-03 06:39:02 +00:00
|
|
|
|
2014-07-18 14:54:43 +00:00
|
|
|
if len(info) == 0 {
|
|
|
|
return nil, ErrNoContainersInPod
|
|
|
|
}
|
|
|
|
|
2015-01-21 00:59:26 +00:00
|
|
|
// First make sure we are not missing pod infra container
|
|
|
|
if _, found := info[PodInfraContainerName]; !found {
|
|
|
|
return nil, ErrNoPodInfraContainerInPod
|
2014-10-03 06:39:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(info) < (len(manifest.Containers) + 1) {
|
|
|
|
var containerStatus api.ContainerStatus
|
2015-02-12 01:03:59 +00:00
|
|
|
// Not all containers expected are created, check if there are
|
2014-10-03 06:39:02 +00:00
|
|
|
// image related issues
|
|
|
|
for _, container := range manifest.Containers {
|
|
|
|
if _, found := info[container.Name]; found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
image := container.Image
|
|
|
|
// Check image is ready on the node or not
|
|
|
|
// TODO(dchen1107): docker/docker/issues/8365 to figure out if the image exists
|
|
|
|
_, err := client.InspectImage(image)
|
2014-10-06 18:54:51 +00:00
|
|
|
if err == nil {
|
|
|
|
containerStatus.State.Waiting = &api.ContainerStateWaiting{
|
|
|
|
Reason: fmt.Sprintf("Image: %s is ready, container is creating", image),
|
|
|
|
}
|
|
|
|
} else if err == docker.ErrNoSuchImage {
|
|
|
|
containerStatus.State.Waiting = &api.ContainerStateWaiting{
|
|
|
|
Reason: fmt.Sprintf("Image: %s is not ready on the node", image),
|
2014-10-03 06:39:02 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-10-06 18:54:51 +00:00
|
|
|
containerStatus.State.Waiting = &api.ContainerStateWaiting{
|
|
|
|
Reason: err.Error(),
|
|
|
|
}
|
2014-10-03 06:39:02 +00:00
|
|
|
}
|
2014-10-06 18:54:51 +00:00
|
|
|
|
2014-10-03 06:39:02 +00:00
|
|
|
info[container.Name] = containerStatus
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-15 17:26:56 +00:00
|
|
|
return info, nil
|
|
|
|
}
|
|
|
|
|
2014-07-15 15:34:48 +00:00
|
|
|
const containerNamePrefix = "k8s"
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
func HashContainer(container *api.Container) uint64 {
|
2014-08-07 23:59:18 +00:00
|
|
|
hash := adler32.New()
|
2015-01-05 23:24:49 +00:00
|
|
|
util.DeepHashObject(hash, *container)
|
2014-08-07 23:59:18 +00:00
|
|
|
return uint64(hash.Sum32())
|
|
|
|
}
|
|
|
|
|
2014-07-18 18:42:47 +00:00
|
|
|
// Creates a name which can be reversed to identify both full pod name and container name.
|
2015-01-14 23:22:21 +00:00
|
|
|
func BuildDockerName(podUID types.UID, podFullName string, container *api.Container) string {
|
2014-09-25 00:05:53 +00:00
|
|
|
containerName := container.Name + "." + strconv.FormatUint(HashContainer(container), 16)
|
2015-01-10 01:19:31 +00:00
|
|
|
return fmt.Sprintf("%s_%s_%s_%s_%08x",
|
|
|
|
containerNamePrefix,
|
|
|
|
containerName,
|
|
|
|
podFullName,
|
|
|
|
podUID,
|
|
|
|
rand.Uint32())
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 21:08:15 +00:00
|
|
|
// TODO(vmarmol): This should probably return an error.
|
2014-09-23 23:33:39 +00:00
|
|
|
// Unpacks a container name, returning the pod full name and container name we would have used to
|
|
|
|
// construct the docker name. If the docker name isn't the one we created, we may return empty strings.
|
2015-01-14 23:22:21 +00:00
|
|
|
func ParseDockerName(name string) (podFullName string, podUID types.UID, containerName string, hash uint64) {
|
2014-07-15 15:34:48 +00:00
|
|
|
// For some reason docker appears to be appending '/' to names.
|
2014-07-28 13:56:03 +00:00
|
|
|
// If it's there, strip it.
|
2014-07-15 15:34:48 +00:00
|
|
|
if name[0] == '/' {
|
|
|
|
name = name[1:]
|
|
|
|
}
|
2014-09-25 00:05:53 +00:00
|
|
|
parts := strings.Split(name, "_")
|
2014-07-15 15:34:48 +00:00
|
|
|
if len(parts) == 0 || parts[0] != containerNamePrefix {
|
|
|
|
return
|
|
|
|
}
|
2015-02-26 20:27:14 +00:00
|
|
|
if len(parts) < 6 {
|
2015-01-10 01:19:31 +00:00
|
|
|
// We have at least 5 fields. We may have more in the future.
|
|
|
|
// Anything with less fields than this is not something we can
|
|
|
|
// manage.
|
2015-02-26 20:27:14 +00:00
|
|
|
glog.Warningf("found a container with the %q prefix, but too few fields (%d): %q", containerNamePrefix, len(parts), name)
|
2015-01-10 01:19:31 +00:00
|
|
|
return
|
2014-07-15 15:34:48 +00:00
|
|
|
}
|
2015-01-10 01:19:31 +00:00
|
|
|
|
|
|
|
// Container name.
|
|
|
|
nameParts := strings.Split(parts[1], ".")
|
|
|
|
containerName = nameParts[0]
|
|
|
|
if len(nameParts) > 1 {
|
|
|
|
var err error
|
|
|
|
hash, err = strconv.ParseUint(nameParts[1], 16, 32)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("invalid container hash: %s", nameParts[1])
|
|
|
|
}
|
2014-09-05 09:49:11 +00:00
|
|
|
}
|
2015-01-10 01:19:31 +00:00
|
|
|
|
|
|
|
// Pod fullname.
|
2015-02-26 20:27:14 +00:00
|
|
|
podFullName = parts[2] + "_" + parts[3]
|
2015-01-10 01:19:31 +00:00
|
|
|
|
|
|
|
// Pod UID.
|
2015-02-26 20:27:14 +00:00
|
|
|
podUID = types.UID(parts[4])
|
2014-07-15 15:34:48 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-03 20:14:16 +00:00
|
|
|
func GetRunningContainers(client DockerInterface, ids []string) ([]*docker.Container, error) {
|
|
|
|
result := []*docker.Container{}
|
|
|
|
if client == nil {
|
|
|
|
return nil, fmt.Errorf("unexpected nil docker client.")
|
|
|
|
}
|
|
|
|
for ix := range ids {
|
|
|
|
status, err := client.InspectContainer(ids[ix])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if status != nil && status.State.Running {
|
|
|
|
result = append(result, status)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2014-07-20 17:31:26 +00:00
|
|
|
// Parses image name including a tag and returns image name and tag.
|
2014-09-05 09:49:11 +00:00
|
|
|
// TODO: Future Docker versions can parse the tag on daemon side, see
|
2014-07-15 15:34:48 +00:00
|
|
|
// https://github.com/dotcloud/docker/issues/6876
|
|
|
|
// So this can be deprecated at some point.
|
|
|
|
func parseImageName(image string) (string, string) {
|
|
|
|
tag := ""
|
|
|
|
parts := strings.SplitN(image, "/", 2)
|
|
|
|
repo := ""
|
|
|
|
if len(parts) == 2 {
|
|
|
|
repo = parts[0]
|
|
|
|
image = parts[1]
|
|
|
|
}
|
|
|
|
parts = strings.SplitN(image, ":", 2)
|
|
|
|
if len(parts) == 2 {
|
|
|
|
image = parts[0]
|
|
|
|
tag = parts[1]
|
|
|
|
}
|
|
|
|
if repo != "" {
|
|
|
|
image = fmt.Sprintf("%s/%s", repo, image)
|
|
|
|
}
|
|
|
|
return image, tag
|
|
|
|
}
|
2014-09-09 04:33:17 +00:00
|
|
|
|
2015-02-05 00:58:26 +00:00
|
|
|
// Get a docker endpoint, either from the string passed in, or $DOCKER_HOST environment variables
|
|
|
|
func getDockerEndpoint(dockerEndpoint string) string {
|
|
|
|
var endpoint string
|
|
|
|
if len(dockerEndpoint) > 0 {
|
|
|
|
endpoint = dockerEndpoint
|
|
|
|
} else if len(os.Getenv("DOCKER_HOST")) > 0 {
|
|
|
|
endpoint = os.Getenv("DOCKER_HOST")
|
|
|
|
} else {
|
|
|
|
endpoint = "unix:///var/run/docker.sock"
|
|
|
|
}
|
|
|
|
glog.Infof("Connecting to docker on %s", endpoint)
|
|
|
|
|
|
|
|
return endpoint
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectToDockerOrDie(dockerEndpoint string) DockerInterface {
|
|
|
|
if dockerEndpoint == "fake://" {
|
|
|
|
return &FakeDockerClient{
|
|
|
|
VersionInfo: []string{"apiVersion=1.16"},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
client, err := docker.NewClient(getDockerEndpoint(dockerEndpoint))
|
|
|
|
if err != nil {
|
|
|
|
glog.Fatal("Couldn't connect to docker.")
|
|
|
|
}
|
|
|
|
return client
|
|
|
|
}
|
|
|
|
|
2014-09-09 04:33:17 +00:00
|
|
|
type ContainerCommandRunner interface {
|
|
|
|
RunInContainer(containerID string, cmd []string) ([]byte, error)
|
2015-02-04 19:50:21 +00:00
|
|
|
GetDockerServerVersion() ([]uint, error)
|
2015-01-08 20:41:38 +00:00
|
|
|
ExecInContainer(containerID string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error
|
|
|
|
PortForward(podInfraContainerID string, port uint16, stream io.ReadWriteCloser) error
|
2014-09-09 04:33:17 +00:00
|
|
|
}
|