Merge pull request #45307 from yujuhong/mv-docker-client

Automatic merge from submit-queue (batch tested with PRs 45453, 45307, 44987)

Migrate the docker client code from dockertools to dockershim

Move docker client code from dockertools to dockershim/libdocker. This includes
DockerInterface (renamed to Interface), FakeDockerClient, etc.

This is part of #43234
pull/6/head
Kubernetes Submit Queue 2017-05-09 20:23:44 -07:00 committed by GitHub
commit 51a3413371
48 changed files with 1077 additions and 882 deletions

View File

@ -56,8 +56,8 @@ go_library(
"//pkg/kubelet/config:go_default_library", "//pkg/kubelet/config:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockershim:go_default_library", "//pkg/kubelet/dockershim:go_default_library",
"//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/dockershim/remote:go_default_library", "//pkg/kubelet/dockershim/remote:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/eviction:go_default_library", "//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/eviction/api:go_default_library", "//pkg/kubelet/eviction/api:go_default_library",
"//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network:go_default_library",

View File

@ -72,8 +72,8 @@ import (
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockershim" "k8s.io/kubernetes/pkg/kubelet/dockershim"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote" dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/eviction" "k8s.io/kubernetes/pkg/kubelet/eviction"
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api" evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
"k8s.io/kubernetes/pkg/kubelet/server" "k8s.io/kubernetes/pkg/kubelet/server"
@ -143,9 +143,9 @@ func UnsecuredKubeletDeps(s *options.KubeletServer) (*kubelet.KubeletDeps, error
writer = &kubeio.NsenterWriter{} writer = &kubeio.NsenterWriter{}
} }
var dockerClient dockertools.DockerInterface var dockerClient libdocker.Interface
if s.ContainerRuntime == "docker" { if s.ContainerRuntime == "docker" {
dockerClient = dockertools.ConnectToDockerOrDie(s.DockerEndpoint, s.RuntimeRequestTimeout.Duration, dockerClient = libdocker.ConnectToDockerOrDie(s.DockerEndpoint, s.RuntimeRequestTimeout.Duration,
s.ImagePullProgressDeadline.Duration) s.ImagePullProgressDeadline.Duration)
} else { } else {
dockerClient = nil dockerClient = nil
@ -937,7 +937,7 @@ func parseResourceList(m componentconfig.ConfigurationMap) (v1.ResourceList, err
// TODO(random-liu): Move this to a separate binary. // TODO(random-liu): Move this to a separate binary.
func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir string) error { func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir string) error {
// Create docker client. // Create docker client.
dockerClient := dockertools.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration, dockerClient := libdocker.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration,
c.ImagePullProgressDeadline.Duration) c.ImagePullProgressDeadline.Duration)
// Initialize network plugin settings. // Initialize network plugin settings.

View File

@ -25,7 +25,7 @@ go_library(
"//pkg/client/metrics/prometheus:go_default_library", "//pkg/client/metrics/prometheus:go_default_library",
"//pkg/kubelet/cadvisor/testing:go_default_library", "//pkg/kubelet/cadvisor/testing:go_default_library",
"//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/cm:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubemark:go_default_library", "//pkg/kubemark:go_default_library",
"//pkg/util/iptables/testing:go_default_library", "//pkg/util/iptables/testing:go_default_library",
"//pkg/version/prometheus:go_default_library", "//pkg/version/prometheus:go_default_library",

View File

@ -33,7 +33,7 @@ import (
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration _ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubemark" "k8s.io/kubernetes/pkg/kubemark"
fakeiptables "k8s.io/kubernetes/pkg/util/iptables/testing" fakeiptables "k8s.io/kubernetes/pkg/util/iptables/testing"
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration _ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
@ -113,7 +113,7 @@ func main() {
cadvisorInterface := new(cadvisortest.Fake) cadvisorInterface := new(cadvisortest.Fake)
containerManager := cm.NewStubContainerManager() containerManager := cm.NewStubContainerManager()
fakeDockerClient := dockertools.NewFakeDockerClient().WithTraceDisabled() fakeDockerClient := libdocker.NewFakeDockerClient().WithTraceDisabled()
fakeDockerClient.EnableSleep = true fakeDockerClient.EnableSleep = true
hollowKubelet := kubemark.NewHollowKubelet( hollowKubelet := kubemark.NewHollowKubelet(

View File

@ -56,8 +56,8 @@ go_library(
"//pkg/kubelet/config:go_default_library", "//pkg/kubelet/config:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockershim:go_default_library", "//pkg/kubelet/dockershim:go_default_library",
"//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/dockershim/remote:go_default_library", "//pkg/kubelet/dockershim/remote:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/envvars:go_default_library", "//pkg/kubelet/envvars:go_default_library",
"//pkg/kubelet/events:go_default_library", "//pkg/kubelet/events:go_default_library",
"//pkg/kubelet/eviction:go_default_library", "//pkg/kubelet/eviction:go_default_library",

View File

@ -39,8 +39,8 @@ go_library(
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockershim/cm:go_default_library", "//pkg/kubelet/dockershim/cm:go_default_library",
"//pkg/kubelet/dockershim/errors:go_default_library", "//pkg/kubelet/dockershim/errors:go_default_library",
"//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/dockershim/securitycontext:go_default_library", "//pkg/kubelet/dockershim/securitycontext:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/leaky:go_default_library", "//pkg/kubelet/leaky:go_default_library",
"//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network:go_default_library",
"//pkg/kubelet/network/cni:go_default_library", "//pkg/kubelet/network/cni:go_default_library",
@ -96,9 +96,9 @@ go_test(
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/container/testing:go_default_library", "//pkg/kubelet/container/testing:go_default_library",
"//pkg/kubelet/dockershim/errors:go_default_library", "//pkg/kubelet/dockershim/errors:go_default_library",
"//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/dockershim/securitycontext:go_default_library", "//pkg/kubelet/dockershim/securitycontext:go_default_library",
"//pkg/kubelet/dockershim/testing:go_default_library", "//pkg/kubelet/dockershim/testing:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network:go_default_library",
"//pkg/kubelet/network/testing:go_default_library", "//pkg/kubelet/network/testing:go_default_library",
"//pkg/kubelet/types:go_default_library", "//pkg/kubelet/types:go_default_library",
@ -128,6 +128,7 @@ filegroup(
":package-srcs", ":package-srcs",
"//pkg/kubelet/dockershim/cm:all-srcs", "//pkg/kubelet/dockershim/cm:all-srcs",
"//pkg/kubelet/dockershim/errors:all-srcs", "//pkg/kubelet/dockershim/errors:all-srcs",
"//pkg/kubelet/dockershim/libdocker:all-srcs",
"//pkg/kubelet/dockershim/remote:all-srcs", "//pkg/kubelet/dockershim/remote:all-srcs",
"//pkg/kubelet/dockershim/securitycontext:all-srcs", "//pkg/kubelet/dockershim/securitycontext:all-srcs",
"//pkg/kubelet/dockershim/testing:all-srcs", "//pkg/kubelet/dockershim/testing:all-srcs",

View File

@ -16,7 +16,7 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/cm:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/qos:go_default_library", "//pkg/kubelet/qos:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",

View File

@ -30,9 +30,10 @@ import (
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
kubecm "k8s.io/kubernetes/pkg/kubelet/cm" kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/qos" "k8s.io/kubernetes/pkg/kubelet/qos"
utilversion "k8s.io/kubernetes/pkg/util/version" utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
const ( const (
@ -50,7 +51,7 @@ var (
memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`) memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`)
) )
func NewContainerManager(cgroupsName string, client dockertools.DockerInterface) ContainerManager { func NewContainerManager(cgroupsName string, client libdocker.Interface) ContainerManager {
return &containerManager{ return &containerManager{
cgroupsName: cgroupsName, cgroupsName: cgroupsName,
client: client, client: client,
@ -59,7 +60,7 @@ func NewContainerManager(cgroupsName string, client dockertools.DockerInterface)
type containerManager struct { type containerManager struct {
// Docker client. // Docker client.
client dockertools.DockerInterface client libdocker.Interface
// Name of the cgroups. // Name of the cgroups.
cgroupsName string cgroupsName string
// Manager for the cgroups. // Manager for the cgroups.

View File

@ -21,13 +21,13 @@ package cm
import ( import (
"fmt" "fmt"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
type unsupportedContainerManager struct { type unsupportedContainerManager struct {
} }
func NewContainerManager(_ string, _ dockertools.DockerInterface) ContainerManager { func NewContainerManager(_ string, _ libdocker.Interface) ContainerManager {
return &unsupportedContainerManager{} return &unsupportedContainerManager{}
} }

View File

@ -24,18 +24,12 @@ import (
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
// This file contains helper functions to convert docker API types to runtime // This file contains helper functions to convert docker API types to runtime
// API types, or vice versa. // API types, or vice versa.
const (
// Status of a container returned by docker ListContainers
statusRunningPrefix = "Up"
statusCreatedPrefix = "Created"
statusExitedPrefix = "Exited"
)
func imageToRuntimeAPIImage(image *dockertypes.Image) (*runtimeapi.Image, error) { func imageToRuntimeAPIImage(image *dockertypes.Image) (*runtimeapi.Image, error) {
if image == nil { if image == nil {
return nil, fmt.Errorf("unable to convert a nil pointer to a runtime API image") return nil, fmt.Errorf("unable to convert a nil pointer to a runtime API image")
@ -126,11 +120,11 @@ func toRuntimeAPIContainerState(state string) runtimeapi.ContainerState {
// Parse the state string in dockertypes.Container. This could break when // Parse the state string in dockertypes.Container. This could break when
// we upgrade docker. // we upgrade docker.
switch { switch {
case strings.HasPrefix(state, statusRunningPrefix): case strings.HasPrefix(state, libdocker.StatusRunningPrefix):
return runtimeapi.ContainerState_CONTAINER_RUNNING return runtimeapi.ContainerState_CONTAINER_RUNNING
case strings.HasPrefix(state, statusExitedPrefix): case strings.HasPrefix(state, libdocker.StatusExitedPrefix):
return runtimeapi.ContainerState_CONTAINER_EXITED return runtimeapi.ContainerState_CONTAINER_EXITED
case strings.HasPrefix(state, statusCreatedPrefix): case strings.HasPrefix(state, libdocker.StatusCreatedPrefix):
return runtimeapi.ContainerState_CONTAINER_CREATED return runtimeapi.ContainerState_CONTAINER_CREATED
default: default:
return runtimeapi.ContainerState_CONTAINER_UNKNOWN return runtimeapi.ContainerState_CONTAINER_UNKNOWN
@ -141,7 +135,7 @@ func toRuntimeAPISandboxState(state string) runtimeapi.PodSandboxState {
// Parse the state string in dockertypes.Container. This could break when // Parse the state string in dockertypes.Container. This could break when
// we upgrade docker. // we upgrade docker.
switch { switch {
case strings.HasPrefix(state, statusRunningPrefix): case strings.HasPrefix(state, libdocker.StatusRunningPrefix):
return runtimeapi.PodSandboxState_SANDBOX_READY return runtimeapi.PodSandboxState_SANDBOX_READY
default: default:
return runtimeapi.PodSandboxState_SANDBOX_NOTREADY return runtimeapi.PodSandboxState_SANDBOX_NOTREADY

View File

@ -29,7 +29,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
// ListContainers lists all containers matching the filter. // ListContainers lists all containers matching the filter.
@ -307,15 +307,15 @@ func getContainerTimestamps(r *dockertypes.ContainerJSON) (time.Time, time.Time,
var createdAt, startedAt, finishedAt time.Time var createdAt, startedAt, finishedAt time.Time
var err error var err error
createdAt, err = dockertools.ParseDockerTimestamp(r.Created) createdAt, err = libdocker.ParseDockerTimestamp(r.Created)
if err != nil { if err != nil {
return createdAt, startedAt, finishedAt, err return createdAt, startedAt, finishedAt, err
} }
startedAt, err = dockertools.ParseDockerTimestamp(r.State.StartedAt) startedAt, err = libdocker.ParseDockerTimestamp(r.State.StartedAt)
if err != nil { if err != nil {
return createdAt, startedAt, finishedAt, err return createdAt, startedAt, finishedAt, err
} }
finishedAt, err = dockertools.ParseDockerTimestamp(r.State.FinishedAt) finishedAt, err = libdocker.ParseDockerTimestamp(r.State.FinishedAt)
if err != nil { if err != nil {
return createdAt, startedAt, finishedAt, err return createdAt, startedAt, finishedAt, err
} }

View File

@ -21,7 +21,8 @@ import (
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
// This file implements methods in ImageManagerService. // This file implements methods in ImageManagerService.
@ -56,7 +57,7 @@ func (ds *dockerService) ListImages(filter *runtimeapi.ImageFilter) ([]*runtimea
func (ds *dockerService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) { func (ds *dockerService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) {
imageInspect, err := ds.client.InspectImageByRef(image.Image) imageInspect, err := ds.client.InspectImageByRef(image.Image)
if err != nil { if err != nil {
if dockertools.IsImageNotFoundError(err) { if libdocker.IsImageNotFoundError(err) {
return nil, nil return nil, nil
} }
return nil, err return nil, err
@ -105,7 +106,7 @@ func (ds *dockerService) RemoveImage(image *runtimeapi.ImageSpec) error {
} }
// getImageRef returns the image digest if exists, or else returns the image ID. // getImageRef returns the image digest if exists, or else returns the image ID.
func getImageRef(client dockertools.DockerInterface, image string) (string, error) { func getImageRef(client libdocker.Interface, image string) (string, error) {
img, err := client.InspectImageByRef(image) img, err := client.InspectImageByRef(image)
if err != nil { if err != nil {
return "", err return "", err

View File

@ -22,7 +22,8 @@ import (
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
func TestRemoveImage(t *testing.T) { func TestRemoveImage(t *testing.T) {
@ -30,8 +31,8 @@ func TestRemoveImage(t *testing.T) {
id := "1111" id := "1111"
fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo"}}}) fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo"}}})
ds.RemoveImage(&runtimeapi.ImageSpec{Image: id}) ds.RemoveImage(&runtimeapi.ImageSpec{Image: id})
fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil), fakeDocker.AssertCallDetails(libdocker.NewCalledDetail("inspect_image", nil),
dockertools.NewCalledDetail("remove_image", []interface{}{id, dockertypes.ImageRemoveOptions{PruneChildren: true}})) libdocker.NewCalledDetail("remove_image", []interface{}{id, dockertypes.ImageRemoveOptions{PruneChildren: true}}))
} }
func TestRemoveImageWithMultipleTags(t *testing.T) { func TestRemoveImageWithMultipleTags(t *testing.T) {
@ -39,7 +40,7 @@ func TestRemoveImageWithMultipleTags(t *testing.T) {
id := "1111" id := "1111"
fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo", "bar"}}}) fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo", "bar"}}})
ds.RemoveImage(&runtimeapi.ImageSpec{Image: id}) ds.RemoveImage(&runtimeapi.ImageSpec{Image: id})
fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil), fakeDocker.AssertCallDetails(libdocker.NewCalledDetail("inspect_image", nil),
dockertools.NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}), libdocker.NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}),
dockertools.NewCalledDetail("remove_image", []interface{}{"bar", dockertypes.ImageRemoveOptions{PruneChildren: true}})) libdocker.NewCalledDetail("remove_image", []interface{}{"bar", dockertypes.ImageRemoveOptions{PruneChildren: true}}))
} }

View File

@ -30,7 +30,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/leaky" "k8s.io/kubernetes/pkg/kubelet/leaky"
) )
@ -70,7 +70,7 @@ func convertLegacyNameAndLabels(names []string, labels map[string]string) ([]str
} }
// Generate new dockershim name. // Generate new dockershim name.
m, _, err := dockertools.ParseDockerName(names[0]) m, _, err := libdocker.ParseDockerName(names[0])
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -23,7 +23,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/types"
) )
@ -105,8 +105,8 @@ func TestConvertLegacyNameAndLabels(t *testing.T) {
} }
// getFakeLegacyContainers returns a list of fake legacy containers. // getFakeLegacyContainers returns a list of fake legacy containers.
func getFakeLegacyContainers() []*dockertools.FakeContainer { func getFakeLegacyContainers() []*libdocker.FakeContainer {
return []*dockertools.FakeContainer{ return []*libdocker.FakeContainer{
{ {
ID: "12", ID: "12",
Name: "k8s_POD.hash1_podname_podnamespace_poduid_randomid", Name: "k8s_POD.hash1_podname_podnamespace_poduid_randomid",
@ -139,8 +139,8 @@ func getFakeLegacyContainers() []*dockertools.FakeContainer {
} }
// getFakeNewContainers returns a list of fake new containers. // getFakeNewContainers returns a list of fake new containers.
func getFakeNewContainers() []*dockertools.FakeContainer { func getFakeNewContainers() []*libdocker.FakeContainer {
return []*dockertools.FakeContainer{ return []*libdocker.FakeContainer{
{ {
ID: "56", ID: "56",
Name: "k8s_POD_podname_podnamespace_poduid_0", Name: "k8s_POD_podname_podnamespace_poduid_0",
@ -233,11 +233,11 @@ func TestListLegacyPodSandbox(t *testing.T) {
func TestCheckLegacyCleanup(t *testing.T) { func TestCheckLegacyCleanup(t *testing.T) {
for desc, test := range map[string]struct { for desc, test := range map[string]struct {
containers []*dockertools.FakeContainer containers []*libdocker.FakeContainer
done bool done bool
}{ }{
"no containers": { "no containers": {
containers: []*dockertools.FakeContainer{}, containers: []*libdocker.FakeContainer{},
done: true, done: true,
}, },
"only new containers": { "only new containers": {

View File

@ -30,7 +30,7 @@ import (
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockershim/errors" "k8s.io/kubernetes/pkg/kubelet/dockershim/errors"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/qos" "k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/types"
) )
@ -161,7 +161,7 @@ func (ds *dockerService) StopPodSandbox(podSandboxID string) error {
// actions will only have sandbox ID and not have pod namespace and name information. // actions will only have sandbox ID and not have pod namespace and name information.
// Return error if encounter any unexpected error. // Return error if encounter any unexpected error.
if checkpointErr != nil { if checkpointErr != nil {
if dockertools.IsContainerNotFoundError(statusErr) && checkpointErr == errors.CheckpointNotFoundError { if libdocker.IsContainerNotFoundError(statusErr) && checkpointErr == errors.CheckpointNotFoundError {
glog.Warningf("Both sandbox container and checkpoint for id %q could not be found. "+ glog.Warningf("Both sandbox container and checkpoint for id %q could not be found. "+
"Proceed without further sandbox information.", podSandboxID) "Proceed without further sandbox information.", podSandboxID)
} else { } else {
@ -206,7 +206,7 @@ func (ds *dockerService) StopPodSandbox(podSandboxID string) error {
if err := ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod); err != nil { if err := ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod); err != nil {
glog.Errorf("Failed to stop sandbox %q: %v", podSandboxID, err) glog.Errorf("Failed to stop sandbox %q: %v", podSandboxID, err)
// Do not return error if the container does not exist // Do not return error if the container does not exist
if !dockertools.IsContainerNotFoundError(err) { if !libdocker.IsContainerNotFoundError(err) {
errList = append(errList, err) errList = append(errList, err)
} }
} }
@ -231,13 +231,13 @@ func (ds *dockerService) RemovePodSandbox(podSandboxID string) error {
// Remove all containers in the sandbox. // Remove all containers in the sandbox.
for i := range containers { for i := range containers {
if err := ds.RemoveContainer(containers[i].ID); err != nil && !dockertools.IsContainerNotFoundError(err) { if err := ds.RemoveContainer(containers[i].ID); err != nil && !libdocker.IsContainerNotFoundError(err) {
errs = append(errs, err) errs = append(errs, err)
} }
} }
// Remove the sandbox container. // Remove the sandbox container.
if err := ds.client.RemoveContainer(podSandboxID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true}); err != nil && !dockertools.IsContainerNotFoundError(err) { if err := ds.client.RemoveContainer(podSandboxID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true}); err != nil && !libdocker.IsContainerNotFoundError(err) {
errs = append(errs, err) errs = append(errs, err)
} }

View File

@ -27,7 +27,7 @@ import (
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/network" "k8s.io/kubernetes/pkg/kubelet/network"
"k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/types"
) )
@ -158,7 +158,7 @@ func TestNetworkPluginInvocation(t *testing.T) {
map[string]string{"label": name}, map[string]string{"label": name},
map[string]string{"annotation": ns}, map[string]string{"annotation": ns},
) )
cID := kubecontainer.ContainerID{Type: runtimeName, ID: dockertools.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))} cID := kubecontainer.ContainerID{Type: runtimeName, ID: libdocker.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))}
mockPlugin.EXPECT().Name().Return("mockNetworkPlugin").AnyTimes() mockPlugin.EXPECT().Name().Return("mockNetworkPlugin").AnyTimes()
setup := mockPlugin.EXPECT().SetUpPod(ns, name, cID) setup := mockPlugin.EXPECT().SetUpPod(ns, name, cID)
@ -196,7 +196,7 @@ func TestHostNetworkPluginInvocation(t *testing.T) {
}, },
}, },
} }
cID := kubecontainer.ContainerID{Type: runtimeName, ID: dockertools.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))} cID := kubecontainer.ContainerID{Type: runtimeName, ID: libdocker.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))}
// No calls to network plugin are expected // No calls to network plugin are expected
_, err := ds.RunPodSandbox(c) _, err := ds.RunPodSandbox(c)
@ -219,7 +219,7 @@ func TestSetUpPodFailure(t *testing.T) {
map[string]string{"label": name}, map[string]string{"label": name},
map[string]string{"annotation": ns}, map[string]string{"annotation": ns},
) )
cID := kubecontainer.ContainerID{Type: runtimeName, ID: dockertools.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))} cID := kubecontainer.ContainerID{Type: runtimeName, ID: libdocker.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))}
mockPlugin.EXPECT().Name().Return("mockNetworkPlugin").AnyTimes() mockPlugin.EXPECT().Name().Return("mockNetworkPlugin").AnyTimes()
mockPlugin.EXPECT().SetUpPod(ns, name, cID).Return(errors.New("setup pod error")).AnyTimes() mockPlugin.EXPECT().SetUpPod(ns, name, cID).Return(errors.New("setup pod error")).AnyTimes()
// Assume network plugin doesn't return error, dockershim should still be able to return not ready correctly. // Assume network plugin doesn't return error, dockershim should still be able to return not ready correctly.

View File

@ -36,23 +36,20 @@ import (
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockershim/cm" "k8s.io/kubernetes/pkg/kubelet/dockershim/cm"
"k8s.io/kubernetes/pkg/kubelet/dockershim/errors" "k8s.io/kubernetes/pkg/kubelet/dockershim/errors"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/network" "k8s.io/kubernetes/pkg/kubelet/network"
"k8s.io/kubernetes/pkg/kubelet/network/cni" "k8s.io/kubernetes/pkg/kubelet/network/cni"
"k8s.io/kubernetes/pkg/kubelet/network/hostport" "k8s.io/kubernetes/pkg/kubelet/network/hostport"
"k8s.io/kubernetes/pkg/kubelet/network/kubenet" "k8s.io/kubernetes/pkg/kubelet/network/kubenet"
"k8s.io/kubernetes/pkg/kubelet/server/streaming" "k8s.io/kubernetes/pkg/kubelet/server/streaming"
"k8s.io/kubernetes/pkg/kubelet/util/cache" "k8s.io/kubernetes/pkg/kubelet/util/cache"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
const ( const (
dockerRuntimeName = "docker" dockerRuntimeName = "docker"
kubeAPIVersion = "0.1.0" kubeAPIVersion = "0.1.0"
// https://docs.docker.com/engine/reference/api/docker_remote_api/
// docker version should be at least 1.10.x
minimumDockerAPIVersion = "1.22.0"
// String used to detect docker host mode for various namespaces (e.g. // String used to detect docker host mode for various namespaces (e.g.
// networking). Must match the value returned by docker inspect -f // networking). Must match the value returned by docker inspect -f
// '{{.HostConfig.NetworkMode}}'. // '{{.HostConfig.NetworkMode}}'.
@ -148,9 +145,9 @@ type dockerNetworkHost struct {
var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey} var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey}
// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process. // NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
func NewDockerService(client dockertools.DockerInterface, seccompProfileRoot string, podSandboxImage string, streamingConfig *streaming.Config, func NewDockerService(client libdocker.Interface, seccompProfileRoot string, podSandboxImage string, streamingConfig *streaming.Config,
pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandlerName, dockershimRootDir string, disableSharedPID bool) (DockerService, error) { pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandlerName, dockershimRootDir string, disableSharedPID bool) (DockerService, error) {
c := dockertools.NewInstrumentedDockerInterface(client) c := libdocker.NewInstrumentedInterface(client)
checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir) checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -246,7 +243,7 @@ type DockerService interface {
type dockerService struct { type dockerService struct {
seccompProfileRoot string seccompProfileRoot string
client dockertools.DockerInterface client libdocker.Interface
os kubecontainer.OSInterface os kubecontainer.OSInterface
podSandboxImage string podSandboxImage string
streamingRuntime *streamingRuntime streamingRuntime *streamingRuntime
@ -413,7 +410,7 @@ func (ds *dockerService) checkVersionCompatibility() error {
return err return err
} }
minAPIVersion, err := semver.Parse(minimumDockerAPIVersion) minAPIVersion, err := semver.Parse(libdocker.MinimumDockerAPIVersion)
if err != nil { if err != nil {
return err return err
} }
@ -421,7 +418,7 @@ func (ds *dockerService) checkVersionCompatibility() error {
// Verify the docker version. // Verify the docker version.
result := apiVersion.Compare(minAPIVersion) result := apiVersion.Compare(minAPIVersion)
if result < 0 { if result < 0 {
return fmt.Errorf("docker API version is older than %s", minimumDockerAPIVersion) return fmt.Errorf("docker API version is older than %s", libdocker.MinimumDockerAPIVersion)
} }
return nil return nil
@ -478,10 +475,10 @@ type DockerLegacyService interface {
// dockerLegacyService implements the DockerLegacyService. We add this for non json-log driver // dockerLegacyService implements the DockerLegacyService. We add this for non json-log driver
// support. (See #41996) // support. (See #41996)
type dockerLegacyService struct { type dockerLegacyService struct {
client dockertools.DockerInterface client libdocker.Interface
} }
func NewDockerLegacyService(client dockertools.DockerInterface) DockerLegacyService { func NewDockerLegacyService(client libdocker.Interface) DockerLegacyService {
return &dockerLegacyService{client: client} return &dockerLegacyService{client: client}
} }
@ -511,7 +508,7 @@ func (d *dockerLegacyService) GetContainerLogs(pod *v1.Pod, containerID kubecont
opts.Tail = strconv.FormatInt(*logOptions.TailLines, 10) opts.Tail = strconv.FormatInt(*logOptions.TailLines, 10)
} }
sopts := dockertools.StreamOptions{ sopts := libdocker.StreamOptions{
OutputStream: stdout, OutputStream: stdout,
ErrorStream: stderr, ErrorStream: stderr,
RawTerminal: container.Config.Tty, RawTerminal: container.Config.Tty,
@ -524,7 +521,7 @@ var criSupportedLogDrivers = []string{"json-file"}
// IsCRISupportedLogDriver checks whether the logging driver used by docker is // IsCRISupportedLogDriver checks whether the logging driver used by docker is
// suppoted by native CRI integration. // suppoted by native CRI integration.
func IsCRISupportedLogDriver(client dockertools.DockerInterface) (bool, error) { func IsCRISupportedLogDriver(client libdocker.Interface) (bool, error) {
info, err := client.Info() info, err := client.Info()
if err != nil { if err != nil {
return false, fmt.Errorf("failed to get docker info: %v", err) return false, fmt.Errorf("failed to get docker info: %v", err)

View File

@ -30,7 +30,7 @@ import (
"k8s.io/client-go/util/clock" "k8s.io/client-go/util/clock"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/network" "k8s.io/kubernetes/pkg/kubelet/network"
nettest "k8s.io/kubernetes/pkg/kubelet/network/testing" nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
"k8s.io/kubernetes/pkg/kubelet/util/cache" "k8s.io/kubernetes/pkg/kubelet/util/cache"
@ -42,15 +42,15 @@ func newTestNetworkPlugin(t *testing.T) *nettest.MockNetworkPlugin {
return nettest.NewMockNetworkPlugin(ctrl) return nettest.NewMockNetworkPlugin(ctrl)
} }
func newTestDockerService() (*dockerService, *dockertools.FakeDockerClient, *clock.FakeClock) { func newTestDockerService() (*dockerService, *libdocker.FakeDockerClient, *clock.FakeClock) {
fakeClock := clock.NewFakeClock(time.Time{}) fakeClock := clock.NewFakeClock(time.Time{})
c := dockertools.NewFakeDockerClient().WithClock(fakeClock).WithVersion("1.11.2", "1.23") c := libdocker.NewFakeDockerClient().WithClock(fakeClock).WithVersion("1.11.2", "1.23")
pm := network.NewPluginManager(&network.NoopNetworkPlugin{}) pm := network.NewPluginManager(&network.NoopNetworkPlugin{})
return &dockerService{client: c, os: &containertest.FakeOS{}, network: pm, return &dockerService{client: c, os: &containertest.FakeOS{}, network: pm,
legacyCleanup: legacyCleanupFlag{done: 1}, checkpointHandler: NewTestPersistentCheckpointHandler()}, c, fakeClock legacyCleanup: legacyCleanupFlag{done: 1}, checkpointHandler: NewTestPersistentCheckpointHandler()}, c, fakeClock
} }
func newTestDockerServiceWithVersionCache() (*dockerService, *dockertools.FakeDockerClient, *clock.FakeClock) { func newTestDockerServiceWithVersionCache() (*dockerService, *libdocker.FakeDockerClient, *clock.FakeClock) {
ds, c, fakeClock := newTestDockerService() ds, c, fakeClock := newTestDockerService()
ds.versionCache = cache.NewObjectCache( ds.versionCache = cache.NewObjectCache(
func() (interface{}, error) { func() (interface{}, error) {

View File

@ -30,13 +30,14 @@ import (
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/server/streaming" "k8s.io/kubernetes/pkg/kubelet/server/streaming"
"k8s.io/kubernetes/pkg/kubelet/util/ioutils" "k8s.io/kubernetes/pkg/kubelet/util/ioutils"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
type streamingRuntime struct { type streamingRuntime struct {
client dockertools.DockerInterface client libdocker.Interface
execHandler ExecHandler execHandler ExecHandler
} }
@ -122,7 +123,7 @@ func (ds *dockerService) PortForward(req *runtimeapi.PortForwardRequest) (*runti
return ds.streamingServer.GetPortForward(req) return ds.streamingServer.GetPortForward(req)
} }
func checkContainerStatus(client dockertools.DockerInterface, containerID string) (*dockertypes.ContainerJSON, error) { func checkContainerStatus(client libdocker.Interface, containerID string) (*dockertypes.ContainerJSON, error) {
container, err := client.InspectContainer(containerID) container, err := client.InspectContainer(containerID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -133,7 +134,7 @@ func checkContainerStatus(client dockertools.DockerInterface, containerID string
return container, nil return container, nil
} }
func attachContainer(client dockertools.DockerInterface, containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error { func attachContainer(client libdocker.Interface, containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
// Have to start this before the call to client.AttachToContainer because client.AttachToContainer is a blocking // Have to start this before the call to client.AttachToContainer because client.AttachToContainer is a blocking
// call :-( Otherwise, resize events don't get processed and the terminal never resizes. // call :-( Otherwise, resize events don't get processed and the terminal never resizes.
kubecontainer.HandleResizing(resize, func(size remotecommand.TerminalSize) { kubecontainer.HandleResizing(resize, func(size remotecommand.TerminalSize) {
@ -147,7 +148,7 @@ func attachContainer(client dockertools.DockerInterface, containerID string, std
Stdout: stdout != nil, Stdout: stdout != nil,
Stderr: stderr != nil, Stderr: stderr != nil,
} }
sopts := dockertools.StreamOptions{ sopts := libdocker.StreamOptions{
InputStream: stdin, InputStream: stdin,
OutputStream: stdout, OutputStream: stdout,
ErrorStream: stderr, ErrorStream: stderr,
@ -156,7 +157,7 @@ func attachContainer(client dockertools.DockerInterface, containerID string, std
return client.AttachToContainer(containerID, opts, sopts) return client.AttachToContainer(containerID, opts, sopts)
} }
func portForward(client dockertools.DockerInterface, podInfraContainerID string, port int32, stream io.ReadWriteCloser) error { func portForward(client libdocker.Interface, podInfraContainerID string, port int32, stream io.ReadWriteCloser) error {
container, err := client.InspectContainer(podInfraContainerID) container, err := client.InspectContainer(podInfraContainerID)
if err != nil { if err != nil {
return err return err

View File

@ -28,14 +28,15 @@ import (
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
utilexec "k8s.io/kubernetes/pkg/util/exec" utilexec "k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/term" "k8s.io/kubernetes/pkg/util/term"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
// ExecHandler knows how to execute a command in a running Docker container. // ExecHandler knows how to execute a command in a running Docker container.
type ExecHandler interface { type ExecHandler interface {
ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error ExecInContainer(client libdocker.Interface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error
} }
// NsenterExecHandler executes commands in Docker containers using nsenter. // NsenterExecHandler executes commands in Docker containers using nsenter.
@ -62,7 +63,7 @@ func (d *dockerExitError) ExitStatus() int {
} }
// TODO should we support nsenter in a container, running with elevated privs and --pid=host? // TODO should we support nsenter in a container, running with elevated privs and --pid=host?
func (*NsenterExecHandler) ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { func (*NsenterExecHandler) ExecInContainer(client libdocker.Interface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
nsenter, err := exec.LookPath("nsenter") nsenter, err := exec.LookPath("nsenter")
if err != nil { if err != nil {
return fmt.Errorf("exec unavailable - unable to locate nsenter") return fmt.Errorf("exec unavailable - unable to locate nsenter")
@ -133,7 +134,7 @@ func (*NsenterExecHandler) ExecInContainer(client dockertools.DockerInterface, c
// NativeExecHandler executes commands in Docker containers using Docker's exec API. // NativeExecHandler executes commands in Docker containers using Docker's exec API.
type NativeExecHandler struct{} type NativeExecHandler struct{}
func (*NativeExecHandler) ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { func (*NativeExecHandler) ExecInContainer(client libdocker.Interface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
createOpts := dockertypes.ExecConfig{ createOpts := dockertypes.ExecConfig{
Cmd: cmd, Cmd: cmd,
AttachStdin: stdin != nil, AttachStdin: stdin != nil,
@ -153,7 +154,7 @@ func (*NativeExecHandler) ExecInContainer(client dockertools.DockerInterface, co
}) })
startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty} startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty}
streamOpts := dockertools.StreamOptions{ streamOpts := libdocker.StreamOptions{
InputStream: stdin, InputStream: stdin,
OutputStream: stdout, OutputStream: stdout,
ErrorStream: stderr, ErrorStream: stderr,

View File

@ -36,9 +36,10 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/security/apparmor" "k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
const ( const (
@ -354,7 +355,7 @@ func getUserFromImageUser(imageUser string) (*int64, string) {
// In that case we have to create the container with a randomized name. // In that case we have to create the container with a randomized name.
// TODO(random-liu): Remove this work around after docker 1.11 is deprecated. // TODO(random-liu): Remove this work around after docker 1.11 is deprecated.
// TODO(#33189): Monitor the tests to see if the fix is sufficient. // TODO(#33189): Monitor the tests to see if the fix is sufficient.
func recoverFromCreationConflictIfNeeded(client dockertools.DockerInterface, createConfig dockertypes.ContainerCreateConfig, err error) (*dockertypes.ContainerCreateResponse, error) { func recoverFromCreationConflictIfNeeded(client libdocker.Interface, createConfig dockertypes.ContainerCreateConfig, err error) (*dockertypes.ContainerCreateResponse, error) {
matches := conflictRE.FindStringSubmatch(err.Error()) matches := conflictRE.FindStringSubmatch(err.Error())
if len(matches) != 2 { if len(matches) != 2 {
return nil, err return nil, err
@ -368,7 +369,7 @@ func recoverFromCreationConflictIfNeeded(client dockertools.DockerInterface, cre
} else { } else {
glog.Errorf("Failed to remove the conflicting container %q: %v", id, rmErr) glog.Errorf("Failed to remove the conflicting container %q: %v", id, rmErr)
// Return if the error is not container not found error. // Return if the error is not container not found error.
if !dockertools.IsContainerNotFoundError(rmErr) { if !libdocker.IsContainerNotFoundError(rmErr) {
return nil, err return nil, err
} }
} }
@ -395,12 +396,12 @@ func getSecurityOptSeparator(v *semver.Version) rune {
} }
// ensureSandboxImageExists pulls the sandbox image when it's not present. // ensureSandboxImageExists pulls the sandbox image when it's not present.
func ensureSandboxImageExists(client dockertools.DockerInterface, image string) error { func ensureSandboxImageExists(client libdocker.Interface, image string) error {
_, err := client.InspectImageByRef(image) _, err := client.InspectImageByRef(image)
if err == nil { if err == nil {
return nil return nil
} }
if !dockertools.IsImageNotFoundError(err) { if !libdocker.IsImageNotFoundError(err) {
return fmt.Errorf("failed to inspect sandbox image %q: %v", image, err) return fmt.Errorf("failed to inspect sandbox image %q: %v", image, err)
} }
err = client.PullImage(image, dockertypes.AuthConfig{}, dockertypes.ImagePullOptions{}) err = client.PullImage(image, dockertypes.AuthConfig{}, dockertypes.ImagePullOptions{})
@ -437,7 +438,7 @@ type dockerOpt struct {
msg string msg string
} }
// Expose key/value from dockertools // Expose key/value from dockerOpt.
func (d dockerOpt) GetKV() (string, string) { func (d dockerOpt) GetKV() (string, string) {
return d.key, d.value return d.key, d.value
} }

View File

@ -29,8 +29,9 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/security/apparmor" "k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
) )
func TestLabelsAndAnnotationsRoundTrip(t *testing.T) { func TestLabelsAndAnnotationsRoundTrip(t *testing.T) {
@ -302,7 +303,7 @@ func TestEnsureSandboxImageExists(t *testing.T) {
}, },
"should pull image when it doesn't exist": { "should pull image when it doesn't exist": {
injectImage: false, injectImage: false,
injectErr: dockertools.ImageNotFoundError{ID: "image_id"}, injectErr: libdocker.ImageNotFoundError{ID: "image_id"},
calls: []string{"inspect_image", "pull"}, calls: []string{"inspect_image", "pull"},
}, },
"should return error when inspect image fails": { "should return error when inspect image fails": {

View File

@ -0,0 +1,69 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"helpers_test.go",
"kube_docker_client_test.go",
"legacy_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/util/hash:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"client.go",
"fake_client.go",
"helpers.go",
"instrumented_client.go",
"kube_docker_client.go",
"legacy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/metrics:go_default_library",
"//vendor/github.com/docker/distribution/digest:go_default_library",
"//vendor/github.com/docker/distribution/reference:go_default_library",
"//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library",
"//vendor/github.com/docker/docker/pkg/stdcopy:go_default_library",
"//vendor/github.com/docker/engine-api/client:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/docker/engine-api/types/container:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/util/clock:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,120 @@
/*
Copyright 2014 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 libdocker
import (
"strings"
"time"
dockerapi "github.com/docker/engine-api/client"
dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog"
)
const (
// https://docs.docker.com/engine/reference/api/docker_remote_api/
// docker version should be at least 1.10.x
MinimumDockerAPIVersion = "1.22.0"
// Status of a container returned by ListContainers.
StatusRunningPrefix = "Up"
StatusCreatedPrefix = "Created"
StatusExitedPrefix = "Exited"
// This is only used by GetKubeletDockerContainers(), and should be removed
// along with the function.
containerNamePrefix = "k8s"
)
// Interface is an abstract interface for testability. It abstracts the interface of docker client.
type Interface interface {
ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error)
InspectContainer(id string) (*dockertypes.ContainerJSON, error)
CreateContainer(dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error)
StartContainer(id string) error
StopContainer(id string, timeout int) error
RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error
InspectImageByRef(imageRef string) (*dockertypes.ImageInspect, error)
InspectImageByID(imageID string) (*dockertypes.ImageInspect, error)
ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error)
PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error
RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error)
ImageHistory(id string) ([]dockertypes.ImageHistory, error)
Logs(string, dockertypes.ContainerLogsOptions, StreamOptions) error
Version() (*dockertypes.Version, error)
Info() (*dockertypes.Info, error)
CreateExec(string, dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error)
StartExec(string, dockertypes.ExecStartCheck, StreamOptions) error
InspectExec(id string) (*dockertypes.ContainerExecInspect, error)
AttachToContainer(string, dockertypes.ContainerAttachOptions, StreamOptions) error
ResizeContainerTTY(id string, height, width int) error
ResizeExecTTY(id string, height, width int) error
}
// Get a *dockerapi.Client, either using the endpoint passed in, or using
// DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT path per their spec
func getDockerClient(dockerEndpoint string) (*dockerapi.Client, error) {
if len(dockerEndpoint) > 0 {
glog.Infof("Connecting to docker on %s", dockerEndpoint)
return dockerapi.NewClient(dockerEndpoint, "", nil, nil)
}
return dockerapi.NewEnvClient()
}
// ConnectToDockerOrDie creates docker client connecting to docker daemon.
// If the endpoint passed in is "fake://", a fake docker client
// will be returned. The program exits if error occurs. The requestTimeout
// is the timeout for docker requests. If timeout is exceeded, the request
// will be cancelled and throw out an error. If requestTimeout is 0, a default
// value will be applied.
func ConnectToDockerOrDie(dockerEndpoint string, requestTimeout, imagePullProgressDeadline time.Duration) Interface {
if dockerEndpoint == "fake://" {
return NewFakeDockerClient()
}
client, err := getDockerClient(dockerEndpoint)
if err != nil {
glog.Fatalf("Couldn't connect to docker: %v", err)
}
glog.Infof("Start docker client with request timeout=%v", requestTimeout)
return newKubeDockerClient(client, requestTimeout, imagePullProgressDeadline)
}
// GetKubeletDockerContainers lists all container or just the running ones.
// Returns a list of docker containers that we manage
// TODO: This function should be deleted after migrating
// test/e2e_node/garbage_collector_test.go off of it.
func GetKubeletDockerContainers(client Interface, allContainers bool) ([]*dockertypes.Container, error) {
result := []*dockertypes.Container{}
containers, err := client.ListContainers(dockertypes.ContainerListOptions{All: allContainers})
if err != nil {
return nil, err
}
for i := range containers {
container := &containers[i]
if len(container.Names) == 0 {
continue
}
// Skip containers that we didn't create to allow users to manually
// spin up their own containers if they want.
if !strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"_") {
glog.V(5).Infof("Docker Container: %s is not managed by kubelet.", container.Names[0])
continue
}
result = append(result, container)
}
return result, nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dockertools package libdocker
import ( import (
"encoding/json" "encoding/json"
@ -77,16 +77,16 @@ type FakeDockerClient struct {
} }
const ( const (
// We don't check docker version now, just set the docker version of fake docker client to 1.8.1.
// Notice that if someday we also have minimum docker version requirement, this should also be updated. // Notice that if someday we also have minimum docker version requirement, this should also be updated.
fakeDockerVersion = "1.8.1" fakeDockerVersion = "1.11.2"
fakeImageSize = 1024 fakeImageSize = 1024
) )
func NewFakeDockerClient() *FakeDockerClient { func NewFakeDockerClient() *FakeDockerClient {
return &FakeDockerClient{ return &FakeDockerClient{
VersionInfo: dockertypes.Version{Version: fakeDockerVersion, APIVersion: minimumDockerAPIVersion}, // Docker's API version does not include the patch number.
VersionInfo: dockertypes.Version{Version: fakeDockerVersion, APIVersion: strings.TrimSuffix(MinimumDockerAPIVersion, ".0")},
Errors: make(map[string]error), Errors: make(map[string]error),
ContainerMap: make(map[string]*dockertypes.ContainerJSON), ContainerMap: make(map[string]*dockertypes.ContainerJSON),
Clock: clock.RealClock{}, Clock: clock.RealClock{},
@ -345,6 +345,14 @@ func (f *FakeDockerClient) AssertImagesPulled(pulled []string) error {
return sortedStringSlicesEqual(pulled, actualPulled) return sortedStringSlicesEqual(pulled, actualPulled)
} }
func (f *FakeDockerClient) AssertImagesPulledMsgs(expected []string) error {
f.Lock()
defer f.Unlock()
// Copy pulled to avoid modifying it.
actual := append([]string{}, f.pulled...)
return sortedStringSlicesEqual(expected, actual)
}
func sortedStringSlicesEqual(expected, actual []string) error { func sortedStringSlicesEqual(expected, actual []string) error {
sort.StringSlice(expected).Sort() sort.StringSlice(expected).Sort()
sort.StringSlice(actual).Sort() sort.StringSlice(actual).Sort()
@ -367,7 +375,7 @@ func (f *FakeDockerClient) popError(op string) error {
} }
} }
// ListContainers is a test-spy implementation of DockerInterface.ListContainers. // ListContainers is a test-spy implementation of Interface.ListContainers.
// It adds an entry "list" to the internal method call record. // It adds an entry "list" to the internal method call record.
func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) { func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
f.Lock() f.Lock()
@ -434,7 +442,7 @@ func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptio
return containerList, err return containerList, err
} }
// InspectContainer is a test-spy implementation of DockerInterface.InspectContainer. // InspectContainer is a test-spy implementation of Interface.InspectContainer.
// It adds an entry "inspect" to the internal method call record. // It adds an entry "inspect" to the internal method call record.
func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJSON, error) { func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
f.Lock() f.Lock()
@ -451,7 +459,7 @@ func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJS
return nil, fmt.Errorf("container %q not found", id) return nil, fmt.Errorf("container %q not found", id)
} }
// InspectImageByRef is a test-spy implementation of DockerInterface.InspectImageByRef. // InspectImageByRef is a test-spy implementation of Interface.InspectImageByRef.
// It adds an entry "inspect" to the internal method call record. // It adds an entry "inspect" to the internal method call record.
func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageInspect, error) { func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageInspect, error) {
f.Lock() f.Lock()
@ -466,7 +474,7 @@ func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageIns
return nil, ImageNotFoundError{name} return nil, ImageNotFoundError{name}
} }
// InspectImageByID is a test-spy implementation of DockerInterface.InspectImageByID. // InspectImageByID is a test-spy implementation of Interface.InspectImageByID.
// It adds an entry "inspect" to the internal method call record. // It adds an entry "inspect" to the internal method call record.
func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInspect, error) { func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInspect, error) {
f.Lock() f.Lock()
@ -502,7 +510,7 @@ func GetFakeContainerID(name string) string {
return strconv.FormatUint(hash.Sum64(), 16) return strconv.FormatUint(hash.Sum64(), 16)
} }
// CreateContainer is a test-spy implementation of DockerInterface.CreateContainer. // CreateContainer is a test-spy implementation of Interface.CreateContainer.
// It adds an entry "create" to the internal method call record. // It adds an entry "create" to the internal method call record.
func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) { func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
f.Lock() f.Lock()
@ -519,7 +527,7 @@ func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig)
timestamp := f.Clock.Now() timestamp := f.Clock.Now()
// The newest container should be in front, because we assume so in GetPodStatus() // The newest container should be in front, because we assume so in GetPodStatus()
f.RunningContainerList = append([]dockertypes.Container{ f.RunningContainerList = append([]dockertypes.Container{
{ID: id, Names: []string{name}, Image: c.Config.Image, Created: timestamp.Unix(), State: statusCreatedPrefix, Labels: c.Config.Labels}, {ID: id, Names: []string{name}, Image: c.Config.Image, Created: timestamp.Unix(), State: StatusCreatedPrefix, Labels: c.Config.Labels},
}, f.RunningContainerList...) }, f.RunningContainerList...)
f.ContainerMap[id] = convertFakeContainer(&FakeContainer{ f.ContainerMap[id] = convertFakeContainer(&FakeContainer{
ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig, CreatedAt: timestamp}) ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig, CreatedAt: timestamp})
@ -529,7 +537,7 @@ func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig)
return &dockertypes.ContainerCreateResponse{ID: id}, nil return &dockertypes.ContainerCreateResponse{ID: id}, nil
} }
// StartContainer is a test-spy implementation of DockerInterface.StartContainer. // StartContainer is a test-spy implementation of Interface.StartContainer.
// It adds an entry "start" to the internal method call record. // It adds an entry "start" to the internal method call record.
func (f *FakeDockerClient) StartContainer(id string) error { func (f *FakeDockerClient) StartContainer(id string) error {
f.Lock() f.Lock()
@ -549,12 +557,12 @@ func (f *FakeDockerClient) StartContainer(id string) error {
container.State.StartedAt = dockerTimestampToString(timestamp) container.State.StartedAt = dockerTimestampToString(timestamp)
container.NetworkSettings.IPAddress = "2.3.4.5" container.NetworkSettings.IPAddress = "2.3.4.5"
f.ContainerMap[id] = container f.ContainerMap[id] = container
f.updateContainerStatus(id, statusRunningPrefix) f.updateContainerStatus(id, StatusRunningPrefix)
f.normalSleep(200, 50, 50) f.normalSleep(200, 50, 50)
return nil return nil
} }
// StopContainer is a test-spy implementation of DockerInterface.StopContainer. // StopContainer is a test-spy implementation of Interface.StopContainer.
// It adds an entry "stop" to the internal method call record. // It adds an entry "stop" to the internal method call record.
func (f *FakeDockerClient) StopContainer(id string, timeout int) error { func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
f.Lock() f.Lock()
@ -565,7 +573,7 @@ func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
} }
f.appendContainerTrace("Stopped", id) f.appendContainerTrace("Stopped", id)
// Container status should be Updated before container moved to ExitedContainerList // Container status should be Updated before container moved to ExitedContainerList
f.updateContainerStatus(id, statusExitedPrefix) f.updateContainerStatus(id, StatusExitedPrefix)
var newList []dockertypes.Container var newList []dockertypes.Container
for _, container := range f.RunningContainerList { for _, container := range f.RunningContainerList {
if container.ID == id { if container.ID == id {
@ -615,7 +623,7 @@ func (f *FakeDockerClient) RemoveContainer(id string, opts dockertypes.Container
return fmt.Errorf("container not stopped") return fmt.Errorf("container not stopped")
} }
// Logs is a test-spy implementation of DockerInterface.Logs. // Logs is a test-spy implementation of Interface.Logs.
// It adds an entry "logs" to the internal method call record. // It adds an entry "logs" to the internal method call record.
func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error { func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
f.Lock() f.Lock()
@ -624,7 +632,7 @@ func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions
return f.popError("logs") return f.popError("logs")
} }
// PullImage is a test-spy implementation of DockerInterface.PullImage. // PullImage is a test-spy implementation of Interface.PullImage.
// It adds an entry "pull" to the internal method call record. // It adds an entry "pull" to the internal method call record.
func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error { func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
f.Lock() f.Lock()
@ -804,7 +812,7 @@ func (f *FakeDockerClient) InjectImageHistory(data map[string][]dockertypes.Imag
// FakeDockerPuller is meant to be a simple wrapper around FakeDockerClient. // FakeDockerPuller is meant to be a simple wrapper around FakeDockerClient.
// Please do not add more functionalities to it. // Please do not add more functionalities to it.
type FakeDockerPuller struct { type FakeDockerPuller struct {
client DockerInterface client Interface
} }
func (f *FakeDockerPuller) Pull(image string, _ []v1.Secret) error { func (f *FakeDockerPuller) Pull(image string, _ []v1.Secret) error {

View File

@ -0,0 +1,131 @@
/*
Copyright 2014 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 libdocker
import (
"strings"
"time"
dockerdigest "github.com/docker/distribution/digest"
dockerref "github.com/docker/distribution/reference"
dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog"
)
// ParseDockerTimestamp parses the timestamp returned by Interface from string to time.Time
func ParseDockerTimestamp(s string) (time.Time, error) {
// Timestamp returned by Docker is in time.RFC3339Nano format.
return time.Parse(time.RFC3339Nano, s)
}
// matchImageTagOrSHA checks if the given image specifier is a valid image ref,
// and that it matches the given image. It should fail on things like image IDs
// (config digests) and other digest-only references, but succeed on image names
// (`foo`), tag references (`foo:bar`), and manifest digest references
// (`foo@sha256:xyz`).
func matchImageTagOrSHA(inspected dockertypes.ImageInspect, image string) bool {
// The image string follows the grammar specified here
// https://github.com/docker/distribution/blob/master/reference/reference.go#L4
named, err := dockerref.ParseNamed(image)
if err != nil {
glog.V(4).Infof("couldn't parse image reference %q: %v", image, err)
return false
}
_, isTagged := named.(dockerref.Tagged)
digest, isDigested := named.(dockerref.Digested)
if !isTagged && !isDigested {
// No Tag or SHA specified, so just return what we have
return true
}
if isTagged {
// Check the RepoTags for a match.
for _, tag := range inspected.RepoTags {
// An image name (without the tag/digest) can be [hostname '/'] component ['/' component]*
// Because either the RepoTag or the name *may* contain the
// hostname or not, we only check for the suffix match.
if strings.HasSuffix(image, tag) || strings.HasSuffix(tag, image) {
return true
}
}
}
if isDigested {
for _, repoDigest := range inspected.RepoDigests {
named, err := dockerref.ParseNamed(repoDigest)
if err != nil {
glog.V(4).Infof("couldn't parse image RepoDigest reference %q: %v", repoDigest, err)
continue
}
if d, isDigested := named.(dockerref.Digested); isDigested {
if digest.Digest().Algorithm().String() == d.Digest().Algorithm().String() &&
digest.Digest().Hex() == d.Digest().Hex() {
return true
}
}
}
// process the ID as a digest
id, err := dockerdigest.ParseDigest(inspected.ID)
if err != nil {
glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err)
return false
}
if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() {
return true
}
}
glog.V(4).Infof("Inspected image (%q) does not match %s", inspected.ID, image)
return false
}
// matchImageIDOnly checks that the given image specifier is a digest-only
// reference, and that it matches the given image.
func matchImageIDOnly(inspected dockertypes.ImageInspect, image string) bool {
// If the image ref is literally equal to the inspected image's ID,
// just return true here (this might be the case for Docker 1.9,
// where we won't have a digest for the ID)
if inspected.ID == image {
return true
}
// Otherwise, we should try actual parsing to be more correct
ref, err := dockerref.Parse(image)
if err != nil {
glog.V(4).Infof("couldn't parse image reference %q: %v", image, err)
return false
}
digest, isDigested := ref.(dockerref.Digested)
if !isDigested {
glog.V(4).Infof("the image reference %q was not a digest reference")
return false
}
id, err := dockerdigest.ParseDigest(inspected.ID)
if err != nil {
glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err)
return false
}
if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() {
return true
}
glog.V(4).Infof("The reference %s does not directly refer to the given image's ID (%q)", image, inspected.ID)
return false
}

View File

@ -0,0 +1,262 @@
/*
Copyright 2014 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 libdocker
import (
"fmt"
"testing"
dockertypes "github.com/docker/engine-api/types"
"github.com/stretchr/testify/assert"
)
func TestMatchImageTagOrSHA(t *testing.T) {
for i, testCase := range []struct {
Inspected dockertypes.ImageInspect
Image string
Output bool
}{
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:latest"}},
Image: "ubuntu",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:14.04"}},
Image: "ubuntu:latest",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "colemickens/hyperkube-amd64:217.9beff63",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "docker.io/colemickens/hyperkube-amd64:217.9beff63",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"docker.io/kubernetes/pause:latest"}},
Image: "kubernetes/pause:latest",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208",
Output: false,
},
{
// mismatched ID is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:0000f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: false,
},
{
// invalid digest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
},
Image: "myimage@sha256:unparseable",
Output: false,
},
{
// v1 schema images can be pulled in one format and returned in another
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// RepoDigest match is is required
Inspected: dockertypes.ImageInspect{
ID: "",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:000084acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
{
// RepoDigest match is allowed
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// RepoDigest and ID are checked
Inspected: dockertypes.ImageInspect{
ID: "sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// unparseable RepoDigests are skipped
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{
"centos/ruby-23-centos7@sha256:unparseable",
"docker.io/centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// unparseable RepoDigest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
{
// unparseable image digest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "centos/ruby-23-centos7@sha256:unparseable",
Output: false,
},
{
// prefix match is rejected for ID and RepoDigest
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "sha256:unparseable",
Output: false,
},
{
// possible SHA prefix match is rejected for ID and RepoDigest because it is not in the named format
Inspected: dockertypes.ImageInspect{
ID: "sha256:0000f247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:0000f247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227"},
},
Image: "sha256:0000",
Output: false,
},
} {
match := matchImageTagOrSHA(testCase.Inspected, testCase.Image)
assert.Equal(t, testCase.Output, match, testCase.Image+fmt.Sprintf(" is not a match (%d)", i))
}
}
func TestMatchImageIDOnly(t *testing.T) {
for i, testCase := range []struct {
Inspected dockertypes.ImageInspect
Image string
Output bool
}{
// shouldn't match names or tagged names
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:latest"}},
Image: "ubuntu",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "colemickens/hyperkube-amd64:217.9beff63",
Output: false,
},
// should match name@digest refs if they refer to the image ID (but only the full ID)
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208",
Output: false,
},
// should match when the IDs are literally the same
{
Inspected: dockertypes.ImageInspect{
ID: "foobar",
},
Image: "foobar",
Output: true,
},
// shouldn't match mismatched IDs
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:0000f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: false,
},
// shouldn't match invalid IDs or refs
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
},
Image: "myimage@sha256:unparseable",
Output: false,
},
// shouldn't match against repo digests
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
} {
match := matchImageIDOnly(testCase.Inspected, testCase.Image)
assert.Equal(t, testCase.Output, match, fmt.Sprintf("%s is not a match (%d)", testCase.Image, i))
}
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dockertools package libdocker
import ( import (
"time" "time"
@ -23,15 +23,15 @@ import (
"k8s.io/kubernetes/pkg/kubelet/metrics" "k8s.io/kubernetes/pkg/kubelet/metrics"
) )
// instrumentedDockerInterface wraps the DockerInterface and records the operations // instrumentedInterface wraps the Interface and records the operations
// and errors metrics. // and errors metrics.
type instrumentedDockerInterface struct { type instrumentedInterface struct {
client DockerInterface client Interface
} }
// Creates an instrumented DockerInterface from an existing DockerInterface. // Creates an instrumented Interface from an existing Interface.
func NewInstrumentedDockerInterface(dockerClient DockerInterface) DockerInterface { func NewInstrumentedInterface(dockerClient Interface) Interface {
return instrumentedDockerInterface{ return instrumentedInterface{
client: dockerClient, client: dockerClient,
} }
} }
@ -53,7 +53,7 @@ func recordError(operation string, err error) {
} }
} }
func (in instrumentedDockerInterface) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) { func (in instrumentedInterface) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
const operation = "list_containers" const operation = "list_containers"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -62,7 +62,7 @@ func (in instrumentedDockerInterface) ListContainers(options dockertypes.Contain
return out, err return out, err
} }
func (in instrumentedDockerInterface) InspectContainer(id string) (*dockertypes.ContainerJSON, error) { func (in instrumentedInterface) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
const operation = "inspect_container" const operation = "inspect_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -71,7 +71,7 @@ func (in instrumentedDockerInterface) InspectContainer(id string) (*dockertypes.
return out, err return out, err
} }
func (in instrumentedDockerInterface) CreateContainer(opts dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) { func (in instrumentedInterface) CreateContainer(opts dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
const operation = "create_container" const operation = "create_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -80,7 +80,7 @@ func (in instrumentedDockerInterface) CreateContainer(opts dockertypes.Container
return out, err return out, err
} }
func (in instrumentedDockerInterface) StartContainer(id string) error { func (in instrumentedInterface) StartContainer(id string) error {
const operation = "start_container" const operation = "start_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -89,7 +89,7 @@ func (in instrumentedDockerInterface) StartContainer(id string) error {
return err return err
} }
func (in instrumentedDockerInterface) StopContainer(id string, timeout int) error { func (in instrumentedInterface) StopContainer(id string, timeout int) error {
const operation = "stop_container" const operation = "stop_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -98,7 +98,7 @@ func (in instrumentedDockerInterface) StopContainer(id string, timeout int) erro
return err return err
} }
func (in instrumentedDockerInterface) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error { func (in instrumentedInterface) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error {
const operation = "remove_container" const operation = "remove_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -107,7 +107,7 @@ func (in instrumentedDockerInterface) RemoveContainer(id string, opts dockertype
return err return err
} }
func (in instrumentedDockerInterface) InspectImageByRef(image string) (*dockertypes.ImageInspect, error) { func (in instrumentedInterface) InspectImageByRef(image string) (*dockertypes.ImageInspect, error) {
const operation = "inspect_image" const operation = "inspect_image"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -116,7 +116,7 @@ func (in instrumentedDockerInterface) InspectImageByRef(image string) (*dockerty
return out, err return out, err
} }
func (in instrumentedDockerInterface) InspectImageByID(image string) (*dockertypes.ImageInspect, error) { func (in instrumentedInterface) InspectImageByID(image string) (*dockertypes.ImageInspect, error) {
const operation = "inspect_image" const operation = "inspect_image"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -125,7 +125,7 @@ func (in instrumentedDockerInterface) InspectImageByID(image string) (*dockertyp
return out, err return out, err
} }
func (in instrumentedDockerInterface) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) { func (in instrumentedInterface) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
const operation = "list_images" const operation = "list_images"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -134,7 +134,7 @@ func (in instrumentedDockerInterface) ListImages(opts dockertypes.ImageListOptio
return out, err return out, err
} }
func (in instrumentedDockerInterface) PullImage(imageID string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error { func (in instrumentedInterface) PullImage(imageID string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
const operation = "pull_image" const operation = "pull_image"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
err := in.client.PullImage(imageID, auth, opts) err := in.client.PullImage(imageID, auth, opts)
@ -142,7 +142,7 @@ func (in instrumentedDockerInterface) PullImage(imageID string, auth dockertypes
return err return err
} }
func (in instrumentedDockerInterface) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) { func (in instrumentedInterface) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) {
const operation = "remove_image" const operation = "remove_image"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -151,7 +151,7 @@ func (in instrumentedDockerInterface) RemoveImage(image string, opts dockertypes
return imageDelete, err return imageDelete, err
} }
func (in instrumentedDockerInterface) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error { func (in instrumentedInterface) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
const operation = "logs" const operation = "logs"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -160,7 +160,7 @@ func (in instrumentedDockerInterface) Logs(id string, opts dockertypes.Container
return err return err
} }
func (in instrumentedDockerInterface) Version() (*dockertypes.Version, error) { func (in instrumentedInterface) Version() (*dockertypes.Version, error) {
const operation = "version" const operation = "version"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -169,7 +169,7 @@ func (in instrumentedDockerInterface) Version() (*dockertypes.Version, error) {
return out, err return out, err
} }
func (in instrumentedDockerInterface) Info() (*dockertypes.Info, error) { func (in instrumentedInterface) Info() (*dockertypes.Info, error) {
const operation = "info" const operation = "info"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -178,7 +178,7 @@ func (in instrumentedDockerInterface) Info() (*dockertypes.Info, error) {
return out, err return out, err
} }
func (in instrumentedDockerInterface) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error) { func (in instrumentedInterface) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error) {
const operation = "create_exec" const operation = "create_exec"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -187,7 +187,7 @@ func (in instrumentedDockerInterface) CreateExec(id string, opts dockertypes.Exe
return out, err return out, err
} }
func (in instrumentedDockerInterface) StartExec(startExec string, opts dockertypes.ExecStartCheck, sopts StreamOptions) error { func (in instrumentedInterface) StartExec(startExec string, opts dockertypes.ExecStartCheck, sopts StreamOptions) error {
const operation = "start_exec" const operation = "start_exec"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -196,7 +196,7 @@ func (in instrumentedDockerInterface) StartExec(startExec string, opts dockertyp
return err return err
} }
func (in instrumentedDockerInterface) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) { func (in instrumentedInterface) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
const operation = "inspect_exec" const operation = "inspect_exec"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -205,7 +205,7 @@ func (in instrumentedDockerInterface) InspectExec(id string) (*dockertypes.Conta
return out, err return out, err
} }
func (in instrumentedDockerInterface) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error { func (in instrumentedInterface) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error {
const operation = "attach" const operation = "attach"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -214,7 +214,7 @@ func (in instrumentedDockerInterface) AttachToContainer(id string, opts dockerty
return err return err
} }
func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) { func (in instrumentedInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
const operation = "image_history" const operation = "image_history"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -223,7 +223,7 @@ func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.Ima
return out, err return out, err
} }
func (in instrumentedDockerInterface) ResizeExecTTY(id string, height, width int) error { func (in instrumentedInterface) ResizeExecTTY(id string, height, width int) error {
const operation = "resize_exec" const operation = "resize_exec"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())
@ -232,7 +232,7 @@ func (in instrumentedDockerInterface) ResizeExecTTY(id string, height, width int
return err return err
} }
func (in instrumentedDockerInterface) ResizeContainerTTY(id string, height, width int) error { func (in instrumentedInterface) ResizeContainerTTY(id string, height, width int) error {
const operation = "resize_container" const operation = "resize_container"
defer recordOperation(operation, time.Now()) defer recordOperation(operation, time.Now())

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dockertools package libdocker
import ( import (
"bytes" "bytes"
@ -38,16 +38,16 @@ import (
// kubeDockerClient is a wrapped layer of docker client for kubelet internal use. This layer is added to: // kubeDockerClient is a wrapped layer of docker client for kubelet internal use. This layer is added to:
// 1) Redirect stream for exec and attach operations. // 1) Redirect stream for exec and attach operations.
// 2) Wrap the context in this layer to make the DockerInterface cleaner. // 2) Wrap the context in this layer to make the Interface cleaner.
// 3) Stabilize the DockerInterface. The engine-api is still under active development, the interface // 3) Stabilize the Interface. The engine-api is still under active development, the interface
// is not stabilized yet. However, the DockerInterface is used in many files in Kubernetes, we may // is not stabilized yet. However, the Interface is used in many files in Kubernetes, we may
// not want to change the interface frequently. With this layer, we can port the engine api to the // not want to change the interface frequently. With this layer, we can port the engine api to the
// DockerInterface to avoid changing DockerInterface as much as possible. // Interface to avoid changing Interface as much as possible.
// (See // (See
// * https://github.com/docker/engine-api/issues/89 // * https://github.com/docker/engine-api/issues/89
// * https://github.com/docker/engine-api/issues/137 // * https://github.com/docker/engine-api/issues/137
// * https://github.com/docker/engine-api/pull/140) // * https://github.com/docker/engine-api/pull/140)
// TODO(random-liu): Swith to new docker interface by refactoring the functions in the old DockerInterface // TODO(random-liu): Swith to new docker interface by refactoring the functions in the old Interface
// one by one. // one by one.
type kubeDockerClient struct { type kubeDockerClient struct {
// timeout is the timeout of short running docker operations. // timeout is the timeout of short running docker operations.
@ -59,8 +59,8 @@ type kubeDockerClient struct {
client *dockerapi.Client client *dockerapi.Client
} }
// Make sure that kubeDockerClient implemented the DockerInterface. // Make sure that kubeDockerClient implemented the Interface.
var _ DockerInterface = &kubeDockerClient{} var _ Interface = &kubeDockerClient{}
// There are 2 kinds of docker operations categorized by running time: // There are 2 kinds of docker operations categorized by running time:
// * Long running operation: The long running operation could run for arbitrary long time, and the running time // * Long running operation: The long running operation could run for arbitrary long time, and the running time
@ -83,7 +83,7 @@ const (
// newKubeDockerClient creates an kubeDockerClient from an existing docker client. If requestTimeout is 0, // newKubeDockerClient creates an kubeDockerClient from an existing docker client. If requestTimeout is 0,
// defaultTimeout will be applied. // defaultTimeout will be applied.
func newKubeDockerClient(dockerClient *dockerapi.Client, requestTimeout, imagePullProgressDeadline time.Duration) DockerInterface { func newKubeDockerClient(dockerClient *dockerapi.Client, requestTimeout, imagePullProgressDeadline time.Duration) Interface {
if requestTimeout == 0 { if requestTimeout == 0 {
requestTimeout = defaultTimeout requestTimeout = defaultTimeout
} }
@ -576,12 +576,6 @@ func (d *kubeDockerClient) getCustomTimeoutContext(timeout time.Duration) (conte
return context.WithTimeout(context.Background(), timeout) return context.WithTimeout(context.Background(), timeout)
} }
// ParseDockerTimestamp parses the timestamp returned by DockerInterface from string to time.Time
func ParseDockerTimestamp(s string) (time.Time, error) {
// Timestamp returned by Docker is in time.RFC3339Nano format.
return time.Parse(time.RFC3339Nano, s)
}
// contextError checks the context, and returns error if the context is timeout. // contextError checks the context, and returns error if the context is timeout.
func contextError(ctx context.Context) error { func contextError(ctx context.Context) error {
if ctx.Err() == context.DeadlineExceeded { if ctx.Err() == context.DeadlineExceeded {

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dockertools package libdocker
import ( import (
"fmt" "fmt"

View File

@ -0,0 +1,92 @@
/*
Copyright 2014 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 libdocker
import (
"fmt"
"math/rand"
"strconv"
"strings"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
)
// This file contains functions used in the non-CRI integration (< 1.6). They
// are currently used for recoginzing containers created by pre-1.6 kubelets.
// TODO: Remove this file for kubernetes 1.8+.
// Creates a name which can be reversed to identify both full pod name and container name.
// This function returns stable name, unique name and a unique id.
// Although rand.Uint32() is not really unique, but it's enough for us because error will
// only occur when instances of the same container in the same pod have the same UID. The
// chance is really slim.
func BuildDockerName(dockerName KubeletContainerName, container *v1.Container) (string, string, string) {
containerName := dockerName.ContainerName + "." + strconv.FormatUint(kubecontainer.HashContainerLegacy(container), 16)
stableName := fmt.Sprintf("%s_%s_%s_%s",
containerNamePrefix,
containerName,
dockerName.PodFullName,
dockerName.PodUID)
UID := fmt.Sprintf("%08x", rand.Uint32())
return stableName, fmt.Sprintf("%s_%s", stableName, UID), UID
}
// Unpacks a container name, returning the pod full name and container name we would have used to
// construct the docker name. If we are unable to parse the name, an error is returned.
func ParseDockerName(name string) (dockerName *KubeletContainerName, hash uint64, err error) {
// For some reason docker appears to be appending '/' to names.
// If it's there, strip it.
name = strings.TrimPrefix(name, "/")
parts := strings.Split(name, "_")
if len(parts) == 0 || parts[0] != containerNamePrefix {
err = fmt.Errorf("failed to parse Docker container name %q into parts", name)
return nil, 0, err
}
if len(parts) < 6 {
// 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.
glog.Warningf("found a container with the %q prefix, but too few fields (%d): %q", containerNamePrefix, len(parts), name)
err = fmt.Errorf("Docker container name %q has less parts than expected %v", name, parts)
return nil, 0, err
}
nameParts := strings.Split(parts[1], ".")
containerName := nameParts[0]
if len(nameParts) > 1 {
hash, err = strconv.ParseUint(nameParts[1], 16, 32)
if err != nil {
glog.Warningf("invalid container hash %q in container %q", nameParts[1], name)
}
}
podFullName := parts[2] + "_" + parts[3]
podUID := types.UID(parts[4])
return &KubeletContainerName{podFullName, podUID, containerName}, hash, nil
}
// KubeletContainerName encapsulates a pod name and a Kubernetes container name.
type KubeletContainerName struct {
PodFullName string
PodUID types.UID
ContainerName string
}

View File

@ -0,0 +1,140 @@
/*
Copyright 2014 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 libdocker
import (
"fmt"
"hash/adler32"
"testing"
dockertypes "github.com/docker/engine-api/types"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1"
hashutil "k8s.io/kubernetes/pkg/util/hash"
)
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) {
assert.New(t).NoError(fakeDocker.AssertCalls(calls))
}
func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
invalid := len(actual) != len(expected)
if !invalid {
for ix, value := range actual {
if expected[ix] != value {
invalid = true
}
}
}
if invalid {
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
}
}
func findPodContainer(dockerContainers []*dockertypes.Container, podFullName string, uid types.UID, containerName string) (*dockertypes.Container, bool, uint64) {
for _, dockerContainer := range dockerContainers {
if len(dockerContainer.Names) == 0 {
continue
}
dockerName, hash, err := ParseDockerName(dockerContainer.Names[0])
if err != nil {
continue
}
if dockerName.PodFullName == podFullName &&
(uid == "" || dockerName.PodUID == uid) &&
dockerName.ContainerName == containerName {
return dockerContainer, true, hash
}
}
return nil, false, 0
}
func TestGetContainerID(t *testing.T) {
fakeDocker := NewFakeDockerClient()
fakeDocker.SetFakeRunningContainers([]*FakeContainer{
{
ID: "foobar",
Name: "/k8s_foo_qux_ns_1234_42",
},
{
ID: "barbar",
Name: "/k8s_bar_qux_ns_2565_42",
},
})
dockerContainers, err := GetKubeletDockerContainers(fakeDocker, false)
if err != nil {
t.Errorf("Expected no error, Got %#v", err)
}
if len(dockerContainers) != 2 {
t.Errorf("Expected %#v, Got %#v", fakeDocker.RunningContainerList, dockerContainers)
}
verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := findPodContainer(dockerContainers, "qux_ns", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
fakeDocker.ClearCalls()
dockerContainer, found, _ = findPodContainer(dockerContainers, "foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found {
t.Errorf("Should not have found container %#v", dockerContainer)
}
}
func verifyPackUnpack(t *testing.T, podNamespace, podUID, podName, containerName string) {
container := &v1.Container{Name: containerName}
hasher := adler32.New()
hashutil.DeepHashObject(hasher, *container)
computedHash := uint64(hasher.Sum32())
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
_, name, _ := BuildDockerName(KubeletContainerName{podFullName, types.UID(podUID), container.Name}, container)
returned, hash, err := ParseDockerName(name)
if err != nil {
t.Errorf("Failed to parse Docker container name %q: %v", name, err)
}
if podFullName != returned.PodFullName || podUID != string(returned.PodUID) || containerName != returned.ContainerName || computedHash != hash {
t.Errorf("For (%s, %s, %s, %d), unpacked (%s, %s, %s, %d)", podFullName, podUID, containerName, computedHash, returned.PodFullName, returned.PodUID, returned.ContainerName, hash)
}
}
func TestContainerNaming(t *testing.T) {
podUID := "12345678"
verifyPackUnpack(t, "file", podUID, "name", "container")
verifyPackUnpack(t, "file", podUID, "name-with-dashes", "container")
// UID is same as pod name
verifyPackUnpack(t, "file", podUID, podUID, "container")
// No Container name
verifyPackUnpack(t, "other", podUID, "name", "")
container := &v1.Container{Name: "container"}
podName := "foo"
podNamespace := "test"
name := fmt.Sprintf("k8s_%s_%s_%s_%s_42", container.Name, podName, podNamespace, podUID)
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
returned, hash, err := ParseDockerName(name)
if err != nil {
t.Errorf("Failed to parse Docker container name %q: %v", name, err)
}
if returned.PodFullName != podFullName || string(returned.PodUID) != podUID || returned.ContainerName != container.Name || hash != 0 {
t.Errorf("unexpected parse: %s %s %s %d", returned.PodFullName, returned.PodUID, returned.ContainerName, hash)
}
}

View File

@ -10,40 +10,23 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = ["docker.go"],
"docker.go",
"fake_docker_client.go",
"instrumented_docker.go",
"kube_docker_client.go",
],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/credentialprovider:go_default_library", "//pkg/credentialprovider:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/metrics:go_default_library",
"//vendor/github.com/docker/distribution/digest:go_default_library",
"//vendor/github.com/docker/distribution/reference:go_default_library",
"//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library", "//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library",
"//vendor/github.com/docker/docker/pkg/stdcopy:go_default_library",
"//vendor/github.com/docker/engine-api/client:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library", "//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/docker/engine-api/types/container:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/client-go/util/clock:go_default_library",
], ],
) )
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = ["docker_test.go"],
"docker_test.go",
"kube_docker_client_test.go",
],
library = ":go_default_library", library = ":go_default_library",
tags = [ tags = [
"automanaged", "automanaged",
@ -51,12 +34,9 @@ go_test(
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/credentialprovider:go_default_library", "//pkg/credentialprovider:go_default_library",
"//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/util/hash:go_default_library",
"//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library", "//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
], ],
) )

View File

@ -18,86 +18,22 @@ package dockertools
import ( import (
"fmt" "fmt"
"math/rand"
"net/http" "net/http"
"path"
"strconv"
"strings" "strings"
"time"
dockerdigest "github.com/docker/distribution/digest"
dockerref "github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
dockerapi "github.com/docker/engine-api/client"
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/credentialprovider" "k8s.io/kubernetes/pkg/credentialprovider"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/images" "k8s.io/kubernetes/pkg/kubelet/images"
) )
const (
LogSuffix = "log"
ext4MaxFileNameLen = 255
DockerType = "docker"
// https://docs.docker.com/engine/reference/api/docker_remote_api/
// docker version should be at least 1.10.x
minimumDockerAPIVersion = "1.22"
statusRunningPrefix = "Up"
statusExitedPrefix = "Exited"
statusCreatedPrefix = "Created"
)
// DockerInterface is an abstract interface for testability. It abstracts the interface of docker client.
type DockerInterface interface {
ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error)
InspectContainer(id string) (*dockertypes.ContainerJSON, error)
CreateContainer(dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error)
StartContainer(id string) error
StopContainer(id string, timeout int) error
RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error
InspectImageByRef(imageRef string) (*dockertypes.ImageInspect, error)
InspectImageByID(imageID string) (*dockertypes.ImageInspect, error)
ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error)
PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error
RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error)
ImageHistory(id string) ([]dockertypes.ImageHistory, error)
Logs(string, dockertypes.ContainerLogsOptions, StreamOptions) error
Version() (*dockertypes.Version, error)
Info() (*dockertypes.Info, error)
CreateExec(string, dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error)
StartExec(string, dockertypes.ExecStartCheck, StreamOptions) error
InspectExec(id string) (*dockertypes.ContainerExecInspect, error)
AttachToContainer(string, dockertypes.ContainerAttachOptions, StreamOptions) error
ResizeContainerTTY(id string, height, width int) error
ResizeExecTTY(id string, height, width int) error
}
// KubeletContainerName encapsulates a pod name and a Kubernetes container name.
type KubeletContainerName struct {
PodFullName string
PodUID types.UID
ContainerName string
}
// containerNamePrefix is used to identify the containers on the node managed by this
// process.
var containerNamePrefix = "k8s"
// SetContainerNamePrefix allows the container prefix name for this process to be changed.
// This is intended to support testing and bootstrapping experimentation. It cannot be
// changed once the Kubelet starts.
func SetContainerNamePrefix(prefix string) {
containerNamePrefix = prefix
}
// DockerPuller is an abstract interface for testability. It abstracts image pull operations. // DockerPuller is an abstract interface for testability. It abstracts image pull operations.
// DockerPuller is *not* in use anywhere in the codebase.
// TODO: Examine whether we can migrate the unit tests and remove the code.
type DockerPuller interface { type DockerPuller interface {
Pull(image string, secrets []v1.Secret) error Pull(image string, secrets []v1.Secret) error
GetImageRef(image string) (string, error) GetImageRef(image string) (string, error)
@ -105,12 +41,12 @@ type DockerPuller interface {
// dockerPuller is the default implementation of DockerPuller. // dockerPuller is the default implementation of DockerPuller.
type dockerPuller struct { type dockerPuller struct {
client DockerInterface client libdocker.Interface
keyring credentialprovider.DockerKeyring keyring credentialprovider.DockerKeyring
} }
// newDockerPuller creates a new instance of the default implementation of DockerPuller. // newDockerPuller creates a new instance of the default implementation of DockerPuller.
func newDockerPuller(client DockerInterface) DockerPuller { func newDockerPuller(client libdocker.Interface) DockerPuller {
return &dockerPuller{ return &dockerPuller{
client: client, client: client,
keyring: credentialprovider.NewDockerKeyring(), keyring: credentialprovider.NewDockerKeyring(),
@ -133,104 +69,6 @@ func filterHTTPError(err error, image string) error {
} }
} }
// matchImageTagOrSHA checks if the given image specifier is a valid image ref,
// and that it matches the given image. It should fail on things like image IDs
// (config digests) and other digest-only references, but succeed on image names
// (`foo`), tag references (`foo:bar`), and manifest digest references
// (`foo@sha256:xyz`).
func matchImageTagOrSHA(inspected dockertypes.ImageInspect, image string) bool {
// The image string follows the grammar specified here
// https://github.com/docker/distribution/blob/master/reference/reference.go#L4
named, err := dockerref.ParseNamed(image)
if err != nil {
glog.V(4).Infof("couldn't parse image reference %q: %v", image, err)
return false
}
_, isTagged := named.(dockerref.Tagged)
digest, isDigested := named.(dockerref.Digested)
if !isTagged && !isDigested {
// No Tag or SHA specified, so just return what we have
return true
}
if isTagged {
// Check the RepoTags for a match.
for _, tag := range inspected.RepoTags {
// An image name (without the tag/digest) can be [hostname '/'] component ['/' component]*
// Because either the RepoTag or the name *may* contain the
// hostname or not, we only check for the suffix match.
if strings.HasSuffix(image, tag) || strings.HasSuffix(tag, image) {
return true
}
}
}
if isDigested {
for _, repoDigest := range inspected.RepoDigests {
named, err := dockerref.ParseNamed(repoDigest)
if err != nil {
glog.V(4).Infof("couldn't parse image RepoDigest reference %q: %v", repoDigest, err)
continue
}
if d, isDigested := named.(dockerref.Digested); isDigested {
if digest.Digest().Algorithm().String() == d.Digest().Algorithm().String() &&
digest.Digest().Hex() == d.Digest().Hex() {
return true
}
}
}
// process the ID as a digest
id, err := dockerdigest.ParseDigest(inspected.ID)
if err != nil {
glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err)
return false
}
if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() {
return true
}
}
glog.V(4).Infof("Inspected image (%q) does not match %s", inspected.ID, image)
return false
}
// matchImageIDOnly checks that the given image specifier is a digest-only
// reference, and that it matches the given image.
func matchImageIDOnly(inspected dockertypes.ImageInspect, image string) bool {
// If the image ref is literally equal to the inspected image's ID,
// just return true here (this might be the case for Docker 1.9,
// where we won't have a digest for the ID)
if inspected.ID == image {
return true
}
// Otherwise, we should try actual parsing to be more correct
ref, err := dockerref.Parse(image)
if err != nil {
glog.V(4).Infof("couldn't parse image reference %q: %v", image, err)
return false
}
digest, isDigested := ref.(dockerref.Digested)
if !isDigested {
glog.V(4).Infof("the image reference %q was not a digest reference")
return false
}
id, err := dockerdigest.ParseDigest(inspected.ID)
if err != nil {
glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err)
return false
}
if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() {
return true
}
glog.V(4).Infof("The reference %s does not directly refer to the given image's ID (%q)", image, inspected.ID)
return false
}
func (p dockerPuller) Pull(image string, secrets []v1.Secret) error { func (p dockerPuller) Pull(image string, secrets []v1.Secret) error {
keyring, err := credentialprovider.MakeDockerKeyring(secrets, p.keyring) keyring, err := credentialprovider.MakeDockerKeyring(secrets, p.keyring)
if err != nil { if err != nil {
@ -296,123 +134,8 @@ func (p dockerPuller) GetImageRef(image string) (string, error) {
} }
return imageRef, nil return imageRef, nil
} }
if IsImageNotFoundError(err) { if libdocker.IsImageNotFoundError(err) {
return "", nil return "", nil
} }
return "", err return "", err
} }
// Creates a name which can be reversed to identify both full pod name and container name.
// This function returns stable name, unique name and a unique id.
// Although rand.Uint32() is not really unique, but it's enough for us because error will
// only occur when instances of the same container in the same pod have the same UID. The
// chance is really slim.
func BuildDockerName(dockerName KubeletContainerName, container *v1.Container) (string, string, string) {
containerName := dockerName.ContainerName + "." + strconv.FormatUint(kubecontainer.HashContainerLegacy(container), 16)
stableName := fmt.Sprintf("%s_%s_%s_%s",
containerNamePrefix,
containerName,
dockerName.PodFullName,
dockerName.PodUID)
UID := fmt.Sprintf("%08x", rand.Uint32())
return stableName, fmt.Sprintf("%s_%s", stableName, UID), UID
}
// Unpacks a container name, returning the pod full name and container name we would have used to
// construct the docker name. If we are unable to parse the name, an error is returned.
func ParseDockerName(name string) (dockerName *KubeletContainerName, hash uint64, err error) {
// For some reason docker appears to be appending '/' to names.
// If it's there, strip it.
name = strings.TrimPrefix(name, "/")
parts := strings.Split(name, "_")
if len(parts) == 0 || parts[0] != containerNamePrefix {
err = fmt.Errorf("failed to parse Docker container name %q into parts", name)
return nil, 0, err
}
if len(parts) < 6 {
// 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.
glog.Warningf("found a container with the %q prefix, but too few fields (%d): %q", containerNamePrefix, len(parts), name)
err = fmt.Errorf("Docker container name %q has less parts than expected %v", name, parts)
return nil, 0, err
}
nameParts := strings.Split(parts[1], ".")
containerName := nameParts[0]
if len(nameParts) > 1 {
hash, err = strconv.ParseUint(nameParts[1], 16, 32)
if err != nil {
glog.Warningf("invalid container hash %q in container %q", nameParts[1], name)
}
}
podFullName := parts[2] + "_" + parts[3]
podUID := types.UID(parts[4])
return &KubeletContainerName{podFullName, podUID, containerName}, hash, nil
}
func LogSymlink(containerLogsDir, podFullName, containerName, dockerId string) string {
suffix := fmt.Sprintf(".%s", LogSuffix)
logPath := fmt.Sprintf("%s_%s-%s", podFullName, containerName, dockerId)
// Length of a filename cannot exceed 255 characters in ext4 on Linux.
if len(logPath) > ext4MaxFileNameLen-len(suffix) {
logPath = logPath[:ext4MaxFileNameLen-len(suffix)]
}
return path.Join(containerLogsDir, logPath+suffix)
}
// Get a *dockerapi.Client, either using the endpoint passed in, or using
// DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT path per their spec
func getDockerClient(dockerEndpoint string) (*dockerapi.Client, error) {
if len(dockerEndpoint) > 0 {
glog.Infof("Connecting to docker on %s", dockerEndpoint)
return dockerapi.NewClient(dockerEndpoint, "", nil, nil)
}
return dockerapi.NewEnvClient()
}
// ConnectToDockerOrDie creates docker client connecting to docker daemon.
// If the endpoint passed in is "fake://", a fake docker client
// will be returned. The program exits if error occurs. The requestTimeout
// is the timeout for docker requests. If timeout is exceeded, the request
// will be cancelled and throw out an error. If requestTimeout is 0, a default
// value will be applied.
func ConnectToDockerOrDie(dockerEndpoint string, requestTimeout, imagePullProgressDeadline time.Duration) DockerInterface {
if dockerEndpoint == "fake://" {
return NewFakeDockerClient()
}
client, err := getDockerClient(dockerEndpoint)
if err != nil {
glog.Fatalf("Couldn't connect to docker: %v", err)
}
glog.Infof("Start docker client with request timeout=%v", requestTimeout)
return newKubeDockerClient(client, requestTimeout, imagePullProgressDeadline)
}
// GetKubeletDockerContainers lists all container or just the running ones.
// Returns a list of docker containers that we manage
// TODO: This function should be deleted after migrating
// test/e2e_node/garbage_collector_test.go off of it.
func GetKubeletDockerContainers(client DockerInterface, allContainers bool) ([]*dockertypes.Container, error) {
result := []*dockertypes.Container{}
containers, err := client.ListContainers(dockertypes.ContainerListOptions{All: allContainers})
if err != nil {
return nil, err
}
for i := range containers {
container := &containers[i]
if len(container.Names) == 0 {
continue
}
// Skip containers that we didn't create to allow users to manually
// spin up their own containers if they want.
if !strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"_") {
glog.V(5).Infof("Docker Container: %s is not managed by kubelet.", container.Names[0])
continue
}
result = append(result, container)
}
return result, nil
}

View File

@ -18,372 +18,17 @@ package dockertools
import ( import (
"encoding/json" "encoding/json"
"fmt"
"hash/adler32"
"math/rand"
"path"
"reflect"
"strings" "strings"
"testing" "testing"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
dockertypes "github.com/docker/engine-api/types"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/credentialprovider" "k8s.io/kubernetes/pkg/credentialprovider"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/images" "k8s.io/kubernetes/pkg/kubelet/images"
hashutil "k8s.io/kubernetes/pkg/util/hash"
) )
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) { // TODO: Examine the tests and see if they can be migrated to kuberuntime.
assert.New(t).NoError(fakeDocker.AssertCalls(calls))
}
func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
invalid := len(actual) != len(expected)
if !invalid {
for ix, value := range actual {
if expected[ix] != value {
invalid = true
}
}
}
if invalid {
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
}
}
func findPodContainer(dockerContainers []*dockertypes.Container, podFullName string, uid types.UID, containerName string) (*dockertypes.Container, bool, uint64) {
for _, dockerContainer := range dockerContainers {
if len(dockerContainer.Names) == 0 {
continue
}
dockerName, hash, err := ParseDockerName(dockerContainer.Names[0])
if err != nil {
continue
}
if dockerName.PodFullName == podFullName &&
(uid == "" || dockerName.PodUID == uid) &&
dockerName.ContainerName == containerName {
return dockerContainer, true, hash
}
}
return nil, false, 0
}
func TestGetContainerID(t *testing.T) {
fakeDocker := NewFakeDockerClient()
fakeDocker.SetFakeRunningContainers([]*FakeContainer{
{
ID: "foobar",
Name: "/k8s_foo_qux_ns_1234_42",
},
{
ID: "barbar",
Name: "/k8s_bar_qux_ns_2565_42",
},
})
dockerContainers, err := GetKubeletDockerContainers(fakeDocker, false)
if err != nil {
t.Errorf("Expected no error, Got %#v", err)
}
if len(dockerContainers) != 2 {
t.Errorf("Expected %#v, Got %#v", fakeDocker.RunningContainerList, dockerContainers)
}
verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := findPodContainer(dockerContainers, "qux_ns", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
fakeDocker.ClearCalls()
dockerContainer, found, _ = findPodContainer(dockerContainers, "foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found {
t.Errorf("Should not have found container %#v", dockerContainer)
}
}
func verifyPackUnpack(t *testing.T, podNamespace, podUID, podName, containerName string) {
container := &v1.Container{Name: containerName}
hasher := adler32.New()
hashutil.DeepHashObject(hasher, *container)
computedHash := uint64(hasher.Sum32())
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
_, name, _ := BuildDockerName(KubeletContainerName{podFullName, types.UID(podUID), container.Name}, container)
returned, hash, err := ParseDockerName(name)
if err != nil {
t.Errorf("Failed to parse Docker container name %q: %v", name, err)
}
if podFullName != returned.PodFullName || podUID != string(returned.PodUID) || containerName != returned.ContainerName || computedHash != hash {
t.Errorf("For (%s, %s, %s, %d), unpacked (%s, %s, %s, %d)", podFullName, podUID, containerName, computedHash, returned.PodFullName, returned.PodUID, returned.ContainerName, hash)
}
}
func TestContainerNaming(t *testing.T) {
podUID := "12345678"
verifyPackUnpack(t, "file", podUID, "name", "container")
verifyPackUnpack(t, "file", podUID, "name-with-dashes", "container")
// UID is same as pod name
verifyPackUnpack(t, "file", podUID, podUID, "container")
// No Container name
verifyPackUnpack(t, "other", podUID, "name", "")
container := &v1.Container{Name: "container"}
podName := "foo"
podNamespace := "test"
name := fmt.Sprintf("k8s_%s_%s_%s_%s_42", container.Name, podName, podNamespace, podUID)
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
returned, hash, err := ParseDockerName(name)
if err != nil {
t.Errorf("Failed to parse Docker container name %q: %v", name, err)
}
if returned.PodFullName != podFullName || string(returned.PodUID) != podUID || returned.ContainerName != container.Name || hash != 0 {
t.Errorf("unexpected parse: %s %s %s %d", returned.PodFullName, returned.PodUID, returned.ContainerName, hash)
}
}
func TestMatchImageTagOrSHA(t *testing.T) {
for i, testCase := range []struct {
Inspected dockertypes.ImageInspect
Image string
Output bool
}{
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:latest"}},
Image: "ubuntu",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:14.04"}},
Image: "ubuntu:latest",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "colemickens/hyperkube-amd64:217.9beff63",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "docker.io/colemickens/hyperkube-amd64:217.9beff63",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"docker.io/kubernetes/pause:latest"}},
Image: "kubernetes/pause:latest",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208",
Output: false,
},
{
// mismatched ID is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:0000f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: false,
},
{
// invalid digest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
},
Image: "myimage@sha256:unparseable",
Output: false,
},
{
// v1 schema images can be pulled in one format and returned in another
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// RepoDigest match is is required
Inspected: dockertypes.ImageInspect{
ID: "",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:000084acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
{
// RepoDigest match is allowed
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// RepoDigest and ID are checked
Inspected: dockertypes.ImageInspect{
ID: "sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// unparseable RepoDigests are skipped
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{
"centos/ruby-23-centos7@sha256:unparseable",
"docker.io/centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: true,
},
{
// unparseable RepoDigest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
{
// unparseable image digest is ignored
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "centos/ruby-23-centos7@sha256:unparseable",
Output: false,
},
{
// prefix match is rejected for ID and RepoDigest
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:unparseable"},
},
Image: "sha256:unparseable",
Output: false,
},
{
// possible SHA prefix match is rejected for ID and RepoDigest because it is not in the named format
Inspected: dockertypes.ImageInspect{
ID: "sha256:0000f247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"docker.io/centos/ruby-23-centos7@sha256:0000f247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227"},
},
Image: "sha256:0000",
Output: false,
},
} {
match := matchImageTagOrSHA(testCase.Inspected, testCase.Image)
assert.Equal(t, testCase.Output, match, testCase.Image+fmt.Sprintf(" is not a match (%d)", i))
}
}
func TestMatchImageIDOnly(t *testing.T) {
for i, testCase := range []struct {
Inspected dockertypes.ImageInspect
Image string
Output bool
}{
// shouldn't match names or tagged names
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"ubuntu:latest"}},
Image: "ubuntu",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{RepoTags: []string{"colemickens/hyperkube-amd64:217.9beff63"}},
Image: "colemickens/hyperkube-amd64:217.9beff63",
Output: false,
},
// should match name@digest refs if they refer to the image ID (but only the full ID)
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: true,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208f7a29005",
Output: false,
},
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:2208",
Output: false,
},
// should match when the IDs are literally the same
{
Inspected: dockertypes.ImageInspect{
ID: "foobar",
},
Image: "foobar",
Output: true,
},
// shouldn't match mismatched IDs
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:2208f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
},
Image: "myimage@sha256:0000f7a29005d226d1ee33a63e33af1f47af6156c740d7d23c7948e8d282d53d",
Output: false,
},
// shouldn't match invalid IDs or refs
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:unparseable",
},
Image: "myimage@sha256:unparseable",
Output: false,
},
// shouldn't match against repo digests
{
Inspected: dockertypes.ImageInspect{
ID: "sha256:9bbdf247c91345f0789c10f50a57e36a667af1189687ad1de88a6243d05a2227",
RepoDigests: []string{"centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf"},
},
Image: "centos/ruby-23-centos7@sha256:940584acbbfb0347272112d2eb95574625c0c60b4e2fdadb139de5859cf754bf",
Output: false,
},
} {
match := matchImageIDOnly(testCase.Inspected, testCase.Image)
assert.Equal(t, testCase.Output, match, fmt.Sprintf("%s is not a match (%d)", testCase.Image, i))
}
}
func TestPullWithNoSecrets(t *testing.T) { func TestPullWithNoSecrets(t *testing.T) {
tests := []struct { tests := []struct {
imageName string imageName string
@ -399,7 +44,7 @@ func TestPullWithNoSecrets(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
fakeKeyring := &credentialprovider.FakeKeyring{} fakeKeyring := &credentialprovider.FakeKeyring{}
fakeClient := NewFakeDockerClient() fakeClient := libdocker.NewFakeDockerClient()
dp := dockerPuller{ dp := dockerPuller{
client: fakeClient, client: fakeClient,
@ -412,13 +57,8 @@ func TestPullWithNoSecrets(t *testing.T) {
continue continue
} }
if e, a := 1, len(fakeClient.pulled); e != a { if err := fakeClient.AssertImagesPulled([]string{test.imageName}); err != nil {
t.Errorf("%s: expected 1 pulled image, got %d: %v", test.imageName, a, fakeClient.pulled) t.Errorf("images pulled do not match the expected: %v", err)
continue
}
if e, a := test.expectedImage, fakeClient.pulled[0]; e != a {
t.Errorf("%s: expected pull of %q, but got %q", test.imageName, e, a)
} }
} }
} }
@ -442,7 +82,7 @@ func TestPullWithJSONError(t *testing.T) {
} }
for i, test := range tests { for i, test := range tests {
fakeKeyring := &credentialprovider.FakeKeyring{} fakeKeyring := &credentialprovider.FakeKeyring{}
fakeClient := NewFakeDockerClient() fakeClient := libdocker.NewFakeDockerClient()
fakeClient.InjectError("pull", test.err) fakeClient.InjectError("pull", test.err)
puller := &dockerPuller{ puller := &dockerPuller{
@ -520,7 +160,7 @@ func TestPullWithSecrets(t *testing.T) {
builtInKeyRing := &credentialprovider.BasicDockerKeyring{} builtInKeyRing := &credentialprovider.BasicDockerKeyring{}
builtInKeyRing.Add(test.builtInDockerConfig) builtInKeyRing.Add(test.builtInDockerConfig)
fakeClient := NewFakeDockerClient() fakeClient := libdocker.NewFakeDockerClient()
dp := dockerPuller{ dp := dockerPuller{
client: fakeClient, client: fakeClient,
@ -532,35 +172,8 @@ func TestPullWithSecrets(t *testing.T) {
t.Errorf("%s: unexpected non-nil err: %s", i, err) t.Errorf("%s: unexpected non-nil err: %s", i, err)
continue continue
} }
if err := fakeClient.AssertImagesPulledMsgs(test.expectedPulls); err != nil {
if e, a := 1, len(fakeClient.pulled); e != a { t.Errorf("images pulled do not match the expected: %v", err)
t.Errorf("%s: expected 1 pulled image, got %d: %v", i, a, fakeClient.pulled)
continue
}
if e, a := test.expectedPulls, fakeClient.pulled; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected pull of %v, but got %v", i, e, a)
} }
} }
} }
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func TestLogSymLink(t *testing.T) {
as := assert.New(t)
containerLogsDir := "/foo/bar"
podFullName := randStringBytes(128)
containerName := randStringBytes(70)
dockerId := randStringBytes(80)
// The file name cannot exceed 255 characters. Since .log suffix is required, the prefix cannot exceed 251 characters.
expectedPath := path.Join(containerLogsDir, fmt.Sprintf("%s_%s-%s", podFullName, containerName, dockerId)[:251]+".log")
as.Equal(expectedPath, LogSymlink(containerLogsDir, podFullName, containerName, dockerId))
}

View File

@ -17,7 +17,7 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/gpu:go_default_library", "//pkg/kubelet/gpu:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",

View File

@ -29,7 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/pkg/kubelet/gpu" "k8s.io/kubernetes/pkg/kubelet/gpu"
) )
@ -60,13 +60,13 @@ type nvidiaGPUManager struct {
defaultDevices []string defaultDevices []string
// The interface which could get GPU mapping from all the containers. // The interface which could get GPU mapping from all the containers.
// TODO: Should make this independent of Docker in the future. // TODO: Should make this independent of Docker in the future.
dockerClient dockertools.DockerInterface dockerClient libdocker.Interface
activePodsLister activePodsLister activePodsLister activePodsLister
} }
// NewNvidiaGPUManager returns a GPUManager that manages local Nvidia GPUs. // NewNvidiaGPUManager returns a GPUManager that manages local Nvidia GPUs.
// TODO: Migrate to use pod level cgroups and make it generic to all runtimes. // TODO: Migrate to use pod level cgroups and make it generic to all runtimes.
func NewNvidiaGPUManager(activePodsLister activePodsLister, dockerClient dockertools.DockerInterface) (gpu.GPUManager, error) { func NewNvidiaGPUManager(activePodsLister activePodsLister, dockerClient libdocker.Interface) (gpu.GPUManager, error) {
if dockerClient == nil { if dockerClient == nil {
return nil, fmt.Errorf("invalid docker client specified") return nil, fmt.Errorf("invalid docker client specified")
} }

View File

@ -63,8 +63,8 @@ import (
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockershim" "k8s.io/kubernetes/pkg/kubelet/dockershim"
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote" dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/eviction" "k8s.io/kubernetes/pkg/kubelet/eviction"
"k8s.io/kubernetes/pkg/kubelet/gpu" "k8s.io/kubernetes/pkg/kubelet/gpu"
@ -219,7 +219,7 @@ type KubeletDeps struct {
CAdvisorInterface cadvisor.Interface CAdvisorInterface cadvisor.Interface
Cloud cloudprovider.Interface Cloud cloudprovider.Interface
ContainerManager cm.ContainerManager ContainerManager cm.ContainerManager
DockerClient dockertools.DockerInterface DockerClient libdocker.Interface
EventClient v1core.EventsGetter EventClient v1core.EventsGetter
KubeClient clientset.Interface KubeClient clientset.Interface
ExternalKubeClient clientgoclientset.Interface ExternalKubeClient clientgoclientset.Interface
@ -793,7 +793,7 @@ type Kubelet struct {
hostname string hostname string
nodeName types.NodeName nodeName types.NodeName
dockerClient dockertools.DockerInterface dockerClient libdocker.Interface
runtimeCache kubecontainer.RuntimeCache runtimeCache kubecontainer.RuntimeCache
kubeClient clientset.Interface kubeClient clientset.Interface
iptClient utilipt.Interface iptClient utilipt.Interface

View File

@ -34,7 +34,6 @@ go_library(
"//pkg/kubelet/api:go_default_library", "//pkg/kubelet/api:go_default_library",
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library", "//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/events:go_default_library", "//pkg/kubelet/events:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library", "//pkg/kubelet/lifecycle:go_default_library",
@ -76,6 +75,7 @@ go_test(
"kuberuntime_manager_test.go", "kuberuntime_manager_test.go",
"kuberuntime_sandbox_test.go", "kuberuntime_sandbox_test.go",
"labels_test.go", "labels_test.go",
"legacy_test.go",
], ],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],

View File

@ -17,8 +17,10 @@ limitations under the License.
package kuberuntime package kuberuntime
import ( import (
"fmt"
"path"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
) )
// This file implements the functions that are needed for backward // This file implements the functions that are needed for backward
@ -30,12 +32,24 @@ const (
// kubelet.containerLogsDir. // kubelet.containerLogsDir.
legacyContainerLogsDir = "/var/log/containers" legacyContainerLogsDir = "/var/log/containers"
// legacyLogSuffix is the legacy log suffix. // legacyLogSuffix is the legacy log suffix.
legacyLogSuffix = dockertools.LogSuffix legacyLogSuffix = "log"
ext4MaxFileNameLen = 255
) )
// legacyLogSymlink composes the legacy container log path. It is only used for legacy cluster // legacyLogSymlink composes the legacy container log path. It is only used for legacy cluster
// logging support. // logging support.
func legacyLogSymlink(containerID string, containerName, podName, podNamespace string) string { func legacyLogSymlink(containerID string, containerName, podName, podNamespace string) string {
return dockertools.LogSymlink(legacyContainerLogsDir, kubecontainer.BuildPodFullName(podName, podNamespace), return logSymlink(legacyContainerLogsDir, kubecontainer.BuildPodFullName(podName, podNamespace),
containerName, containerID) containerName, containerID)
} }
func logSymlink(containerLogsDir, podFullName, containerName, dockerId string) string {
suffix := fmt.Sprintf(".%s", legacyLogSuffix)
logPath := fmt.Sprintf("%s_%s-%s", podFullName, containerName, dockerId)
// Length of a filename cannot exceed 255 characters in ext4 on Linux.
if len(logPath) > ext4MaxFileNameLen-len(suffix) {
logPath = logPath[:ext4MaxFileNameLen-len(suffix)]
}
return path.Join(containerLogsDir, logPath+suffix)
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2017 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 kuberuntime
import (
"fmt"
"math/rand"
"path"
"testing"
"github.com/stretchr/testify/assert"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func TestLogSymLink(t *testing.T) {
as := assert.New(t)
containerLogsDir := "/foo/bar"
podFullName := randStringBytes(128)
containerName := randStringBytes(70)
dockerId := randStringBytes(80)
// The file name cannot exceed 255 characters. Since .log suffix is required, the prefix cannot exceed 251 characters.
expectedPath := path.Join(containerLogsDir, fmt.Sprintf("%s_%s-%s", podFullName, containerName, dockerId)[:251]+".log")
as.Equal(expectedPath, logSymlink(containerLogsDir, podFullName, containerName, dockerId))
}

View File

@ -22,7 +22,6 @@ go_library(
"//pkg/api/validation:go_default_library", "//pkg/api/validation:go_default_library",
"//pkg/apis/extensions/validation:go_default_library", "//pkg/apis/extensions/validation:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library", "//pkg/kubelet/lifecycle:go_default_library",
], ],
) )

View File

@ -21,7 +21,6 @@ import (
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
"k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/lifecycle" "k8s.io/kubernetes/pkg/kubelet/lifecycle"
) )
@ -31,8 +30,13 @@ const (
// (e.g., 1.24). Append the version with a ".0" so that it works // (e.g., 1.24). Append the version with a ".0" so that it works
// with both the CRI and dockertools comparison logic. // with both the CRI and dockertools comparison logic.
dockerMinimumAPIVersion = "1.24.0" dockerMinimumAPIVersion = "1.24.0"
dockerTypeName = "docker"
) )
// TODO: The admission logic in this file is runtime-dependent. It should be
// changed to be generic and CRI-compatible.
type runtimeAdmitHandler struct { type runtimeAdmitHandler struct {
result lifecycle.PodAdmitResult result lifecycle.PodAdmitResult
} }
@ -42,7 +46,7 @@ var _ lifecycle.PodAdmitHandler = &runtimeAdmitHandler{}
// NewRuntimeAdmitHandler returns a sysctlRuntimeAdmitHandler which checks whether // NewRuntimeAdmitHandler returns a sysctlRuntimeAdmitHandler which checks whether
// the given runtime support sysctls. // the given runtime support sysctls.
func NewRuntimeAdmitHandler(runtime container.Runtime) (*runtimeAdmitHandler, error) { func NewRuntimeAdmitHandler(runtime container.Runtime) (*runtimeAdmitHandler, error) {
if runtime.Type() == dockertools.DockerType { if runtime.Type() == dockerTypeName {
v, err := runtime.APIVersion() v, err := runtime.APIVersion()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get runtime version: %v", err) return nil, fmt.Errorf("failed to get runtime version: %v", err)

View File

@ -27,7 +27,7 @@ go_library(
"//pkg/kubelet/cadvisor:go_default_library", "//pkg/kubelet/cadvisor:go_default_library",
"//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/cm:go_default_library",
"//pkg/kubelet/container/testing:go_default_library", "//pkg/kubelet/container/testing:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/types:go_default_library", "//pkg/kubelet/types:go_default_library",
"//pkg/util:go_default_library", "//pkg/util:go_default_library",
"//pkg/util/io:go_default_library", "//pkg/util/io:go_default_library",

View File

@ -30,7 +30,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/cadvisor" "k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types" kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
kubeio "k8s.io/kubernetes/pkg/util/io" kubeio "k8s.io/kubernetes/pkg/util/io"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
@ -52,7 +52,7 @@ func NewHollowKubelet(
nodeName string, nodeName string,
client *clientset.Clientset, client *clientset.Clientset,
cadvisorInterface cadvisor.Interface, cadvisorInterface cadvisor.Interface,
dockerClient dockertools.DockerInterface, dockerClient libdocker.Interface,
kubeletPort, kubeletReadOnlyPort int, kubeletPort, kubeletReadOnlyPort int,
containerManager cm.ContainerManager, containerManager cm.ContainerManager,
maxPods int, podsPerCore int, maxPods int, podsPerCore int,

View File

@ -101,7 +101,7 @@ go_test(
"//pkg/kubelet/api/v1alpha1/stats:go_default_library", "//pkg/kubelet/api/v1alpha1/stats:go_default_library",
"//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/cm:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockershim/libdocker:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/metrics:go_default_library", "//pkg/kubelet/metrics:go_default_library",
"//pkg/kubelet/types:go_default_library", "//pkg/kubelet/types:go_default_library",

View File

@ -23,7 +23,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
docker "k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -258,16 +258,16 @@ func containerGCTest(f *framework.Framework, test testRun) {
// Runs containerGCTest using the docker runtime. // Runs containerGCTest using the docker runtime.
func dockerContainerGCTest(f *framework.Framework, test testRun) { func dockerContainerGCTest(f *framework.Framework, test testRun) {
var runtime docker.DockerInterface var runtime libdocker.Interface
BeforeEach(func() { BeforeEach(func() {
runtime = docker.ConnectToDockerOrDie(defaultDockerEndpoint, defaultRuntimeRequestTimeoutDuration, defaultImagePullProgressDeadline) runtime = libdocker.ConnectToDockerOrDie(defaultDockerEndpoint, defaultRuntimeRequestTimeoutDuration, defaultImagePullProgressDeadline)
}) })
for _, pod := range test.testPods { for _, pod := range test.testPods {
// Initialize the getContainerNames function to use the dockertools api // Initialize the getContainerNames function to use the dockertools api
thisPrefix := pod.containerPrefix thisPrefix := pod.containerPrefix
pod.getContainerNames = func() ([]string, error) { pod.getContainerNames = func() ([]string, error) {
relevantContainers := []string{} relevantContainers := []string{}
dockerContainers, err := docker.GetKubeletDockerContainers(runtime, true) dockerContainers, err := libdocker.GetKubeletDockerContainers(runtime, true)
if err != nil { if err != nil {
return relevantContainers, err return relevantContainers, err
} }