2016-07-22 21:24:05 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package dockershim
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2016-07-25 21:39:31 +00:00
|
|
|
"time"
|
2016-07-22 21:24:05 +00:00
|
|
|
|
|
|
|
dockertypes "github.com/docker/engine-api/types"
|
|
|
|
dockercontainer "github.com/docker/engine-api/types/container"
|
|
|
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
|
|
|
dockerstrslice "github.com/docker/engine-api/types/strslice"
|
2016-08-27 02:04:09 +00:00
|
|
|
"github.com/golang/glog"
|
2016-07-25 21:39:31 +00:00
|
|
|
|
2016-07-22 21:24:05 +00:00
|
|
|
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
2016-07-25 21:39:31 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
2016-07-22 21:24:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ListContainers lists all containers matching the filter.
|
|
|
|
func (ds *dockerService) ListContainers(filter *runtimeApi.ContainerFilter) ([]*runtimeApi.Container, error) {
|
|
|
|
opts := dockertypes.ContainerListOptions{All: true}
|
|
|
|
|
|
|
|
opts.Filter = dockerfilters.NewArgs()
|
2016-07-25 23:25:36 +00:00
|
|
|
f := newDockerFilter(&opts.Filter)
|
2016-09-15 19:05:54 +00:00
|
|
|
// Add filter to get *only* (non-sandbox) containers.
|
|
|
|
f.AddLabel(containerTypeLabelKey, containerTypeLabelContainer)
|
2016-07-25 23:25:36 +00:00
|
|
|
|
2016-07-22 21:24:05 +00:00
|
|
|
if filter != nil {
|
|
|
|
if filter.Id != nil {
|
2016-07-25 23:25:36 +00:00
|
|
|
f.Add("id", filter.GetId())
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
if filter.State != nil {
|
2016-07-25 23:25:36 +00:00
|
|
|
f.Add("status", toDockerContainerStatus(filter.GetState()))
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
if filter.PodSandboxId != nil {
|
2016-09-16 00:31:51 +00:00
|
|
|
f.AddLabel(sandboxIDLabelKey, *filter.PodSandboxId)
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if filter.LabelSelector != nil {
|
|
|
|
for k, v := range filter.LabelSelector {
|
2016-07-25 23:25:36 +00:00
|
|
|
f.AddLabel(k, v)
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
containers, err := ds.client.ListContainers(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Convert docker to runtime api containers.
|
|
|
|
result := []*runtimeApi.Container{}
|
2016-08-26 23:15:06 +00:00
|
|
|
for i := range containers {
|
|
|
|
c := containers[i]
|
2016-08-17 08:04:25 +00:00
|
|
|
|
2016-08-27 02:04:09 +00:00
|
|
|
converted, err := toRuntimeAPIContainer(&c)
|
|
|
|
if err != nil {
|
|
|
|
glog.V(5).Infof("Unable to convert docker to runtime API container: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
2016-09-13 14:08:03 +00:00
|
|
|
|
2016-08-27 02:04:09 +00:00
|
|
|
result = append(result, converted)
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateContainer creates a new container in the given PodSandbox
|
|
|
|
// Note: docker doesn't use LogPath yet.
|
|
|
|
// TODO: check if the default values returned by the runtime API are ok.
|
|
|
|
func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) {
|
|
|
|
if config == nil {
|
|
|
|
return "", fmt.Errorf("container config is nil")
|
|
|
|
}
|
|
|
|
if sandboxConfig == nil {
|
2016-08-17 08:04:25 +00:00
|
|
|
return "", fmt.Errorf("sandbox config is nil for container %q", config.Metadata.GetName())
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
labels := makeLabels(config.GetLabels(), config.GetAnnotations())
|
|
|
|
// Apply a the container type label.
|
|
|
|
labels[containerTypeLabelKey] = containerTypeLabelContainer
|
2016-09-16 00:31:51 +00:00
|
|
|
// Write the sandbox ID in the labels.
|
|
|
|
labels[sandboxIDLabelKey] = podSandboxID
|
2016-07-22 21:24:05 +00:00
|
|
|
|
|
|
|
image := ""
|
|
|
|
if iSpec := config.GetImage(); iSpec != nil {
|
|
|
|
image = iSpec.GetImage()
|
|
|
|
}
|
|
|
|
createConfig := dockertypes.ContainerCreateConfig{
|
2016-08-27 02:04:09 +00:00
|
|
|
Name: makeContainerName(sandboxConfig, config),
|
2016-07-22 21:24:05 +00:00
|
|
|
Config: &dockercontainer.Config{
|
|
|
|
// TODO: set User.
|
|
|
|
Entrypoint: dockerstrslice.StrSlice(config.GetCommand()),
|
|
|
|
Cmd: dockerstrslice.StrSlice(config.GetArgs()),
|
|
|
|
Env: generateEnvList(config.GetEnvs()),
|
|
|
|
Image: image,
|
|
|
|
WorkingDir: config.GetWorkingDir(),
|
|
|
|
Labels: labels,
|
|
|
|
// Interactive containers:
|
|
|
|
OpenStdin: config.GetStdin(),
|
|
|
|
StdinOnce: config.GetStdinOnce(),
|
|
|
|
Tty: config.GetTty(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill the HostConfig.
|
|
|
|
hc := &dockercontainer.HostConfig{
|
|
|
|
Binds: generateMountBindings(config.GetMounts()),
|
|
|
|
ReadonlyRootfs: config.GetReadonlyRootfs(),
|
|
|
|
Privileged: config.GetPrivileged(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply options derived from the sandbox config.
|
|
|
|
if lc := sandboxConfig.GetLinux(); lc != nil {
|
|
|
|
// Apply Cgroup options.
|
|
|
|
// TODO: Check if this works with per-pod cgroups.
|
|
|
|
hc.CgroupParent = lc.GetCgroupParent()
|
|
|
|
|
|
|
|
// Apply namespace options.
|
|
|
|
sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID)
|
|
|
|
hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode)
|
|
|
|
hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode)
|
|
|
|
hc.UTSMode = ""
|
|
|
|
hc.PidMode = ""
|
|
|
|
|
|
|
|
nsOpts := lc.GetNamespaceOptions()
|
|
|
|
if nsOpts != nil {
|
|
|
|
if nsOpts.GetHostNetwork() {
|
|
|
|
hc.UTSMode = namespaceModeHost
|
|
|
|
}
|
|
|
|
if nsOpts.GetHostPid() {
|
|
|
|
hc.PidMode = namespaceModeHost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply Linux-specific options if applicable.
|
|
|
|
if lc := config.GetLinux(); lc != nil {
|
|
|
|
// Apply resource options.
|
|
|
|
// TODO: Check if the units are correct.
|
|
|
|
// TODO: Can we assume the defaults are sane?
|
|
|
|
rOpts := lc.GetResources()
|
|
|
|
if rOpts != nil {
|
|
|
|
hc.Resources = dockercontainer.Resources{
|
|
|
|
Memory: rOpts.GetMemoryLimitInBytes(),
|
|
|
|
MemorySwap: -1, // Always disable memory swap.
|
|
|
|
CPUShares: rOpts.GetCpuShares(),
|
|
|
|
CPUQuota: rOpts.GetCpuQuota(),
|
|
|
|
CPUPeriod: rOpts.GetCpuPeriod(),
|
|
|
|
// TODO: Need to set devices.
|
|
|
|
}
|
|
|
|
hc.OomScoreAdj = int(rOpts.GetOomScoreAdj())
|
|
|
|
}
|
|
|
|
// Note: ShmSize is handled in kube_docker_client.go
|
|
|
|
}
|
|
|
|
|
2016-07-25 23:25:36 +00:00
|
|
|
hc.SecurityOpt = []string{getSeccompOpts()}
|
2016-07-22 21:24:05 +00:00
|
|
|
// TODO: Add or drop capabilities.
|
|
|
|
|
|
|
|
createConfig.HostConfig = hc
|
|
|
|
createResp, err := ds.client.CreateContainer(createConfig)
|
|
|
|
if createResp != nil {
|
|
|
|
return createResp.ID, err
|
|
|
|
}
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartContainer starts the container.
|
2016-08-05 23:26:47 +00:00
|
|
|
func (ds *dockerService) StartContainer(containerID string) error {
|
|
|
|
return ds.client.StartContainer(containerID)
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StopContainer stops a running container with a grace period (i.e., timeout).
|
2016-08-05 23:26:47 +00:00
|
|
|
func (ds *dockerService) StopContainer(containerID string, timeout int64) error {
|
|
|
|
return ds.client.StopContainer(containerID, int(timeout))
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveContainer removes the container.
|
|
|
|
// TODO: If a container is still running, should we forcibly remove it?
|
2016-08-05 23:26:47 +00:00
|
|
|
func (ds *dockerService) RemoveContainer(containerID string) error {
|
|
|
|
return ds.client.RemoveContainer(containerID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 21:39:31 +00:00
|
|
|
func getContainerTimestamps(r *dockertypes.ContainerJSON) (time.Time, time.Time, time.Time, error) {
|
|
|
|
var createdAt, startedAt, finishedAt time.Time
|
|
|
|
var err error
|
|
|
|
|
|
|
|
createdAt, err = dockertools.ParseDockerTimestamp(r.Created)
|
|
|
|
if err != nil {
|
|
|
|
return createdAt, startedAt, finishedAt, err
|
|
|
|
}
|
|
|
|
startedAt, err = dockertools.ParseDockerTimestamp(r.State.StartedAt)
|
|
|
|
if err != nil {
|
|
|
|
return createdAt, startedAt, finishedAt, err
|
|
|
|
}
|
|
|
|
finishedAt, err = dockertools.ParseDockerTimestamp(r.State.FinishedAt)
|
|
|
|
if err != nil {
|
|
|
|
return createdAt, startedAt, finishedAt, err
|
|
|
|
}
|
|
|
|
return createdAt, startedAt, finishedAt, nil
|
|
|
|
}
|
|
|
|
|
2016-07-22 21:24:05 +00:00
|
|
|
// ContainerStatus returns the container status.
|
2016-08-05 23:26:47 +00:00
|
|
|
func (ds *dockerService) ContainerStatus(containerID string) (*runtimeApi.ContainerStatus, error) {
|
|
|
|
r, err := ds.client.InspectContainer(containerID)
|
2016-07-25 21:39:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the timstamps.
|
|
|
|
createdAt, startedAt, finishedAt, err := getContainerTimestamps(r)
|
|
|
|
if err != nil {
|
2016-08-05 23:26:47 +00:00
|
|
|
return nil, fmt.Errorf("failed to parse timestamp for container %q: %v", containerID, err)
|
2016-07-25 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the mounts.
|
|
|
|
mounts := []*runtimeApi.Mount{}
|
|
|
|
for _, m := range r.Mounts {
|
|
|
|
readonly := !m.RW
|
|
|
|
mounts = append(mounts, &runtimeApi.Mount{
|
|
|
|
Name: &m.Name,
|
|
|
|
HostPath: &m.Source,
|
|
|
|
ContainerPath: &m.Destination,
|
|
|
|
Readonly: &readonly,
|
|
|
|
// Note: Can't set SeLinuxRelabel
|
|
|
|
})
|
|
|
|
}
|
|
|
|
// Interpret container states.
|
|
|
|
var state runtimeApi.ContainerState
|
2016-09-23 19:32:21 +00:00
|
|
|
var reason, message string
|
2016-07-25 21:39:31 +00:00
|
|
|
if r.State.Running {
|
|
|
|
// Container is running.
|
|
|
|
state = runtimeApi.ContainerState_RUNNING
|
|
|
|
} else {
|
|
|
|
// Container is *not* running. We need to get more details.
|
|
|
|
// * Case 1: container has run and exited with non-zero finishedAt
|
|
|
|
// time.
|
|
|
|
// * Case 2: container has failed to start; it has a zero finishedAt
|
|
|
|
// time, but a non-zero exit code.
|
|
|
|
// * Case 3: container has been created, but not started (yet).
|
|
|
|
if !finishedAt.IsZero() { // Case 1
|
|
|
|
state = runtimeApi.ContainerState_EXITED
|
|
|
|
switch {
|
|
|
|
case r.State.OOMKilled:
|
|
|
|
// TODO: consider exposing OOMKilled via the runtimeAPI.
|
|
|
|
// Note: if an application handles OOMKilled gracefully, the
|
|
|
|
// exit code could be zero.
|
|
|
|
reason = "OOMKilled"
|
|
|
|
case r.State.ExitCode == 0:
|
|
|
|
reason = "Completed"
|
|
|
|
default:
|
2016-09-23 19:32:21 +00:00
|
|
|
reason = "Error"
|
2016-07-25 21:39:31 +00:00
|
|
|
}
|
2016-09-23 19:32:21 +00:00
|
|
|
} else if r.State.ExitCode != 0 { // Case 2
|
2016-07-25 21:39:31 +00:00
|
|
|
state = runtimeApi.ContainerState_EXITED
|
|
|
|
// Adjust finshedAt and startedAt time to createdAt time to avoid
|
|
|
|
// the confusion.
|
|
|
|
finishedAt, startedAt = createdAt, createdAt
|
|
|
|
reason = "ContainerCannotRun"
|
|
|
|
} else { // Case 3
|
|
|
|
state = runtimeApi.ContainerState_CREATED
|
|
|
|
}
|
2016-09-23 19:32:21 +00:00
|
|
|
message = r.State.Error
|
2016-07-25 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to unix timestamps.
|
|
|
|
ct, st, ft := createdAt.Unix(), startedAt.Unix(), finishedAt.Unix()
|
|
|
|
exitCode := int32(r.State.ExitCode)
|
|
|
|
|
2016-08-27 02:04:09 +00:00
|
|
|
metadata, err := parseContainerName(r.Name)
|
2016-08-17 08:04:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-09-15 20:08:51 +00:00
|
|
|
labels, annotations := extractLabels(r.Config.Labels)
|
2016-07-25 21:39:31 +00:00
|
|
|
return &runtimeApi.ContainerStatus{
|
2016-09-15 20:08:51 +00:00
|
|
|
Id: &r.ID,
|
|
|
|
Metadata: metadata,
|
|
|
|
Image: &runtimeApi.ImageSpec{Image: &r.Config.Image},
|
|
|
|
ImageRef: &r.Image,
|
|
|
|
Mounts: mounts,
|
|
|
|
ExitCode: &exitCode,
|
|
|
|
State: &state,
|
|
|
|
CreatedAt: &ct,
|
|
|
|
StartedAt: &st,
|
|
|
|
FinishedAt: &ft,
|
|
|
|
Reason: &reason,
|
2016-09-23 19:32:21 +00:00
|
|
|
Message: &message,
|
2016-09-15 20:08:51 +00:00
|
|
|
Labels: labels,
|
|
|
|
Annotations: annotations,
|
2016-07-25 21:39:31 +00:00
|
|
|
}, nil
|
2016-07-22 21:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Exec execute a command in the container.
|
2016-07-25 21:39:31 +00:00
|
|
|
// TODO: Need to handle terminal resizing before implementing this function.
|
|
|
|
// https://github.com/kubernetes/kubernetes/issues/29579.
|
2016-08-05 23:26:47 +00:00
|
|
|
func (ds *dockerService) Exec(containerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error {
|
2016-07-22 21:24:05 +00:00
|
|
|
return fmt.Errorf("not implemented")
|
|
|
|
}
|