mirror of https://github.com/k3s-io/k3s
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 #43234pull/6/head
commit
51a3413371
|
@ -56,8 +56,8 @@ go_library(
|
|||
"//pkg/kubelet/config:go_default_library",
|
||||
"//pkg/kubelet/container: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/dockertools:go_default_library",
|
||||
"//pkg/kubelet/eviction:go_default_library",
|
||||
"//pkg/kubelet/eviction/api:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
|
|
|
@ -72,8 +72,8 @@ import (
|
|||
"k8s.io/kubernetes/pkg/kubelet/config"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
dockerremote "k8s.io/kubernetes/pkg/kubelet/dockershim/remote"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
||||
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server"
|
||||
|
@ -143,9 +143,9 @@ func UnsecuredKubeletDeps(s *options.KubeletServer) (*kubelet.KubeletDeps, error
|
|||
writer = &kubeio.NsenterWriter{}
|
||||
}
|
||||
|
||||
var dockerClient dockertools.DockerInterface
|
||||
var dockerClient libdocker.Interface
|
||||
if s.ContainerRuntime == "docker" {
|
||||
dockerClient = dockertools.ConnectToDockerOrDie(s.DockerEndpoint, s.RuntimeRequestTimeout.Duration,
|
||||
dockerClient = libdocker.ConnectToDockerOrDie(s.DockerEndpoint, s.RuntimeRequestTimeout.Duration,
|
||||
s.ImagePullProgressDeadline.Duration)
|
||||
} else {
|
||||
dockerClient = nil
|
||||
|
@ -937,7 +937,7 @@ func parseResourceList(m componentconfig.ConfigurationMap) (v1.ResourceList, err
|
|||
// TODO(random-liu): Move this to a separate binary.
|
||||
func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir string) error {
|
||||
// Create docker client.
|
||||
dockerClient := dockertools.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration,
|
||||
dockerClient := libdocker.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration,
|
||||
c.ImagePullProgressDeadline.Duration)
|
||||
|
||||
// Initialize network plugin settings.
|
||||
|
|
|
@ -25,7 +25,7 @@ go_library(
|
|||
"//pkg/client/metrics/prometheus:go_default_library",
|
||||
"//pkg/kubelet/cadvisor/testing: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/util/iptables/testing:go_default_library",
|
||||
"//pkg/version/prometheus:go_default_library",
|
||||
|
|
|
@ -33,7 +33,7 @@ import (
|
|||
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
|
||||
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubemark"
|
||||
fakeiptables "k8s.io/kubernetes/pkg/util/iptables/testing"
|
||||
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
|
||||
|
@ -113,7 +113,7 @@ func main() {
|
|||
cadvisorInterface := new(cadvisortest.Fake)
|
||||
containerManager := cm.NewStubContainerManager()
|
||||
|
||||
fakeDockerClient := dockertools.NewFakeDockerClient().WithTraceDisabled()
|
||||
fakeDockerClient := libdocker.NewFakeDockerClient().WithTraceDisabled()
|
||||
fakeDockerClient.EnableSleep = true
|
||||
|
||||
hollowKubelet := kubemark.NewHollowKubelet(
|
||||
|
|
|
@ -56,8 +56,8 @@ go_library(
|
|||
"//pkg/kubelet/config:go_default_library",
|
||||
"//pkg/kubelet/container: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/dockertools:go_default_library",
|
||||
"//pkg/kubelet/envvars:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/eviction:go_default_library",
|
||||
|
|
|
@ -39,8 +39,8 @@ go_library(
|
|||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/cm: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/dockertools:go_default_library",
|
||||
"//pkg/kubelet/leaky:go_default_library",
|
||||
"//pkg/kubelet/network: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/testing: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/testing:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/testing:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
|
@ -128,6 +128,7 @@ filegroup(
|
|||
":package-srcs",
|
||||
"//pkg/kubelet/dockershim/cm:all-srcs",
|
||||
"//pkg/kubelet/dockershim/errors:all-srcs",
|
||||
"//pkg/kubelet/dockershim/libdocker:all-srcs",
|
||||
"//pkg/kubelet/dockershim/remote:all-srcs",
|
||||
"//pkg/kubelet/dockershim/securitycontext:all-srcs",
|
||||
"//pkg/kubelet/dockershim/testing:all-srcs",
|
||||
|
|
|
@ -16,7 +16,7 @@ go_library(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//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/util/version:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
|
|
|
@ -30,9 +30,10 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -50,7 +51,7 @@ var (
|
|||
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{
|
||||
cgroupsName: cgroupsName,
|
||||
client: client,
|
||||
|
@ -59,7 +60,7 @@ func NewContainerManager(cgroupsName string, client dockertools.DockerInterface)
|
|||
|
||||
type containerManager struct {
|
||||
// Docker client.
|
||||
client dockertools.DockerInterface
|
||||
client libdocker.Interface
|
||||
// Name of the cgroups.
|
||||
cgroupsName string
|
||||
// Manager for the cgroups.
|
||||
|
|
|
@ -21,13 +21,13 @@ package cm
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
type unsupportedContainerManager struct {
|
||||
}
|
||||
|
||||
func NewContainerManager(_ string, _ dockertools.DockerInterface) ContainerManager {
|
||||
func NewContainerManager(_ string, _ libdocker.Interface) ContainerManager {
|
||||
return &unsupportedContainerManager{}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,18 +24,12 @@ import (
|
|||
dockertypes "github.com/docker/engine-api/types"
|
||||
|
||||
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
|
||||
// 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) {
|
||||
if image == nil {
|
||||
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
|
||||
// we upgrade docker.
|
||||
switch {
|
||||
case strings.HasPrefix(state, statusRunningPrefix):
|
||||
case strings.HasPrefix(state, libdocker.StatusRunningPrefix):
|
||||
return runtimeapi.ContainerState_CONTAINER_RUNNING
|
||||
case strings.HasPrefix(state, statusExitedPrefix):
|
||||
case strings.HasPrefix(state, libdocker.StatusExitedPrefix):
|
||||
return runtimeapi.ContainerState_CONTAINER_EXITED
|
||||
case strings.HasPrefix(state, statusCreatedPrefix):
|
||||
case strings.HasPrefix(state, libdocker.StatusCreatedPrefix):
|
||||
return runtimeapi.ContainerState_CONTAINER_CREATED
|
||||
default:
|
||||
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
|
||||
// we upgrade docker.
|
||||
switch {
|
||||
case strings.HasPrefix(state, statusRunningPrefix):
|
||||
case strings.HasPrefix(state, libdocker.StatusRunningPrefix):
|
||||
return runtimeapi.PodSandboxState_SANDBOX_READY
|
||||
default:
|
||||
return runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
|
||||
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.
|
||||
|
@ -307,15 +307,15 @@ func getContainerTimestamps(r *dockertypes.ContainerJSON) (time.Time, time.Time,
|
|||
var createdAt, startedAt, finishedAt time.Time
|
||||
var err error
|
||||
|
||||
createdAt, err = dockertools.ParseDockerTimestamp(r.Created)
|
||||
createdAt, err = libdocker.ParseDockerTimestamp(r.Created)
|
||||
if err != nil {
|
||||
return createdAt, startedAt, finishedAt, err
|
||||
}
|
||||
startedAt, err = dockertools.ParseDockerTimestamp(r.State.StartedAt)
|
||||
startedAt, err = libdocker.ParseDockerTimestamp(r.State.StartedAt)
|
||||
if err != nil {
|
||||
return createdAt, startedAt, finishedAt, err
|
||||
}
|
||||
finishedAt, err = dockertools.ParseDockerTimestamp(r.State.FinishedAt)
|
||||
finishedAt, err = libdocker.ParseDockerTimestamp(r.State.FinishedAt)
|
||||
if err != nil {
|
||||
return createdAt, startedAt, finishedAt, err
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ import (
|
|||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
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.
|
||||
|
@ -56,7 +57,7 @@ func (ds *dockerService) ListImages(filter *runtimeapi.ImageFilter) ([]*runtimea
|
|||
func (ds *dockerService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) {
|
||||
imageInspect, err := ds.client.InspectImageByRef(image.Image)
|
||||
if err != nil {
|
||||
if dockertools.IsImageNotFoundError(err) {
|
||||
if libdocker.IsImageNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
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.
|
||||
func getImageRef(client dockertools.DockerInterface, image string) (string, error) {
|
||||
func getImageRef(client libdocker.Interface, image string) (string, error) {
|
||||
img, err := client.InspectImageByRef(image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -22,7 +22,8 @@ import (
|
|||
dockertypes "github.com/docker/engine-api/types"
|
||||
|
||||
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) {
|
||||
|
@ -30,8 +31,8 @@ func TestRemoveImage(t *testing.T) {
|
|||
id := "1111"
|
||||
fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo"}}})
|
||||
ds.RemoveImage(&runtimeapi.ImageSpec{Image: id})
|
||||
fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil),
|
||||
dockertools.NewCalledDetail("remove_image", []interface{}{id, dockertypes.ImageRemoveOptions{PruneChildren: true}}))
|
||||
fakeDocker.AssertCallDetails(libdocker.NewCalledDetail("inspect_image", nil),
|
||||
libdocker.NewCalledDetail("remove_image", []interface{}{id, dockertypes.ImageRemoveOptions{PruneChildren: true}}))
|
||||
}
|
||||
|
||||
func TestRemoveImageWithMultipleTags(t *testing.T) {
|
||||
|
@ -39,7 +40,7 @@ func TestRemoveImageWithMultipleTags(t *testing.T) {
|
|||
id := "1111"
|
||||
fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo", "bar"}}})
|
||||
ds.RemoveImage(&runtimeapi.ImageSpec{Image: id})
|
||||
fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil),
|
||||
dockertools.NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}),
|
||||
dockertools.NewCalledDetail("remove_image", []interface{}{"bar", dockertypes.ImageRemoveOptions{PruneChildren: true}}))
|
||||
fakeDocker.AssertCallDetails(libdocker.NewCalledDetail("inspect_image", nil),
|
||||
libdocker.NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}),
|
||||
libdocker.NewCalledDetail("remove_image", []interface{}{"bar", dockertypes.ImageRemoveOptions{PruneChildren: true}}))
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -70,7 +70,7 @@ func convertLegacyNameAndLabels(names []string, labels map[string]string) ([]str
|
|||
}
|
||||
|
||||
// Generate new dockershim name.
|
||||
m, _, err := dockertools.ParseDockerName(names[0])
|
||||
m, _, err := libdocker.ParseDockerName(names[0])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
|
@ -105,8 +105,8 @@ func TestConvertLegacyNameAndLabels(t *testing.T) {
|
|||
}
|
||||
|
||||
// getFakeLegacyContainers returns a list of fake legacy containers.
|
||||
func getFakeLegacyContainers() []*dockertools.FakeContainer {
|
||||
return []*dockertools.FakeContainer{
|
||||
func getFakeLegacyContainers() []*libdocker.FakeContainer {
|
||||
return []*libdocker.FakeContainer{
|
||||
{
|
||||
ID: "12",
|
||||
Name: "k8s_POD.hash1_podname_podnamespace_poduid_randomid",
|
||||
|
@ -139,8 +139,8 @@ func getFakeLegacyContainers() []*dockertools.FakeContainer {
|
|||
}
|
||||
|
||||
// getFakeNewContainers returns a list of fake new containers.
|
||||
func getFakeNewContainers() []*dockertools.FakeContainer {
|
||||
return []*dockertools.FakeContainer{
|
||||
func getFakeNewContainers() []*libdocker.FakeContainer {
|
||||
return []*libdocker.FakeContainer{
|
||||
{
|
||||
ID: "56",
|
||||
Name: "k8s_POD_podname_podnamespace_poduid_0",
|
||||
|
@ -233,11 +233,11 @@ func TestListLegacyPodSandbox(t *testing.T) {
|
|||
|
||||
func TestCheckLegacyCleanup(t *testing.T) {
|
||||
for desc, test := range map[string]struct {
|
||||
containers []*dockertools.FakeContainer
|
||||
containers []*libdocker.FakeContainer
|
||||
done bool
|
||||
}{
|
||||
"no containers": {
|
||||
containers: []*dockertools.FakeContainer{},
|
||||
containers: []*libdocker.FakeContainer{},
|
||||
done: true,
|
||||
},
|
||||
"only new containers": {
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"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/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.
|
||||
// Return error if encounter any unexpected error.
|
||||
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. "+
|
||||
"Proceed without further sandbox information.", podSandboxID)
|
||||
} else {
|
||||
|
@ -206,7 +206,7 @@ func (ds *dockerService) StopPodSandbox(podSandboxID string) error {
|
|||
if err := ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod); err != nil {
|
||||
glog.Errorf("Failed to stop sandbox %q: %v", podSandboxID, err)
|
||||
// Do not return error if the container does not exist
|
||||
if !dockertools.IsContainerNotFoundError(err) {
|
||||
if !libdocker.IsContainerNotFoundError(err) {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
|
@ -231,13 +231,13 @@ func (ds *dockerService) RemovePodSandbox(podSandboxID string) error {
|
|||
|
||||
// Remove all containers in the sandbox.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
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/types"
|
||||
)
|
||||
|
@ -158,7 +158,7 @@ func TestNetworkPluginInvocation(t *testing.T) {
|
|||
map[string]string{"label": name},
|
||||
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()
|
||||
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
|
||||
_, err := ds.RunPodSandbox(c)
|
||||
|
@ -219,7 +219,7 @@ func TestSetUpPodFailure(t *testing.T) {
|
|||
map[string]string{"label": name},
|
||||
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().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.
|
||||
|
|
|
@ -36,23 +36,20 @@ import (
|
|||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/cm"
|
||||
"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/cni"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network/hostport"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network/kubenet"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/cache"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
const (
|
||||
dockerRuntimeName = "docker"
|
||||
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.
|
||||
// networking). Must match the value returned by docker inspect -f
|
||||
// '{{.HostConfig.NetworkMode}}'.
|
||||
|
@ -148,9 +145,9 @@ type dockerNetworkHost struct {
|
|||
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.
|
||||
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) {
|
||||
c := dockertools.NewInstrumentedDockerInterface(client)
|
||||
c := libdocker.NewInstrumentedInterface(client)
|
||||
checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -246,7 +243,7 @@ type DockerService interface {
|
|||
|
||||
type dockerService struct {
|
||||
seccompProfileRoot string
|
||||
client dockertools.DockerInterface
|
||||
client libdocker.Interface
|
||||
os kubecontainer.OSInterface
|
||||
podSandboxImage string
|
||||
streamingRuntime *streamingRuntime
|
||||
|
@ -413,7 +410,7 @@ func (ds *dockerService) checkVersionCompatibility() error {
|
|||
return err
|
||||
}
|
||||
|
||||
minAPIVersion, err := semver.Parse(minimumDockerAPIVersion)
|
||||
minAPIVersion, err := semver.Parse(libdocker.MinimumDockerAPIVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -421,7 +418,7 @@ func (ds *dockerService) checkVersionCompatibility() error {
|
|||
// Verify the docker version.
|
||||
result := apiVersion.Compare(minAPIVersion)
|
||||
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
|
||||
|
@ -478,10 +475,10 @@ type DockerLegacyService interface {
|
|||
// dockerLegacyService implements the DockerLegacyService. We add this for non json-log driver
|
||||
// support. (See #41996)
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -511,7 +508,7 @@ func (d *dockerLegacyService) GetContainerLogs(pod *v1.Pod, containerID kubecont
|
|||
opts.Tail = strconv.FormatInt(*logOptions.TailLines, 10)
|
||||
}
|
||||
|
||||
sopts := dockertools.StreamOptions{
|
||||
sopts := libdocker.StreamOptions{
|
||||
OutputStream: stdout,
|
||||
ErrorStream: stderr,
|
||||
RawTerminal: container.Config.Tty,
|
||||
|
@ -524,7 +521,7 @@ var criSupportedLogDrivers = []string{"json-file"}
|
|||
|
||||
// IsCRISupportedLogDriver checks whether the logging driver used by docker is
|
||||
// suppoted by native CRI integration.
|
||||
func IsCRISupportedLogDriver(client dockertools.DockerInterface) (bool, error) {
|
||||
func IsCRISupportedLogDriver(client libdocker.Interface) (bool, error) {
|
||||
info, err := client.Info()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get docker info: %v", err)
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"k8s.io/client-go/util/clock"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
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"
|
||||
nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/cache"
|
||||
|
@ -42,15 +42,15 @@ func newTestNetworkPlugin(t *testing.T) *nettest.MockNetworkPlugin {
|
|||
return nettest.NewMockNetworkPlugin(ctrl)
|
||||
}
|
||||
|
||||
func newTestDockerService() (*dockerService, *dockertools.FakeDockerClient, *clock.FakeClock) {
|
||||
func newTestDockerService() (*dockerService, *libdocker.FakeDockerClient, *clock.FakeClock) {
|
||||
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{})
|
||||
return &dockerService{client: c, os: &containertest.FakeOS{}, network: pm,
|
||||
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.versionCache = cache.NewObjectCache(
|
||||
func() (interface{}, error) {
|
||||
|
|
|
@ -30,13 +30,14 @@ import (
|
|||
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
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/util/ioutils"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
type streamingRuntime struct {
|
||||
client dockertools.DockerInterface
|
||||
client libdocker.Interface
|
||||
execHandler ExecHandler
|
||||
}
|
||||
|
||||
|
@ -122,7 +123,7 @@ func (ds *dockerService) PortForward(req *runtimeapi.PortForwardRequest) (*runti
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -133,7 +134,7 @@ func checkContainerStatus(client dockertools.DockerInterface, containerID string
|
|||
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
|
||||
// call :-( Otherwise, resize events don't get processed and the terminal never resizes.
|
||||
kubecontainer.HandleResizing(resize, func(size remotecommand.TerminalSize) {
|
||||
|
@ -147,7 +148,7 @@ func attachContainer(client dockertools.DockerInterface, containerID string, std
|
|||
Stdout: stdout != nil,
|
||||
Stderr: stderr != nil,
|
||||
}
|
||||
sopts := dockertools.StreamOptions{
|
||||
sopts := libdocker.StreamOptions{
|
||||
InputStream: stdin,
|
||||
OutputStream: stdout,
|
||||
ErrorStream: stderr,
|
||||
|
@ -156,7 +157,7 @@ func attachContainer(client dockertools.DockerInterface, containerID string, std
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -28,14 +28,15 @@ import (
|
|||
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
"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.
|
||||
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.
|
||||
|
@ -62,7 +63,7 @@ func (d *dockerExitError) ExitStatus() int {
|
|||
}
|
||||
|
||||
// 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")
|
||||
if err != nil {
|
||||
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.
|
||||
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{
|
||||
Cmd: cmd,
|
||||
AttachStdin: stdin != nil,
|
||||
|
@ -153,7 +154,7 @@ func (*NativeExecHandler) ExecInContainer(client dockertools.DockerInterface, co
|
|||
})
|
||||
|
||||
startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty}
|
||||
streamOpts := dockertools.StreamOptions{
|
||||
streamOpts := libdocker.StreamOptions{
|
||||
InputStream: stdin,
|
||||
OutputStream: stdout,
|
||||
ErrorStream: stderr,
|
||||
|
|
|
@ -36,9 +36,10 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
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/security/apparmor"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -354,7 +355,7 @@ func getUserFromImageUser(imageUser string) (*int64, string) {
|
|||
// 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(#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())
|
||||
if len(matches) != 2 {
|
||||
return nil, err
|
||||
|
@ -368,7 +369,7 @@ func recoverFromCreationConflictIfNeeded(client dockertools.DockerInterface, cre
|
|||
} else {
|
||||
glog.Errorf("Failed to remove the conflicting container %q: %v", id, rmErr)
|
||||
// Return if the error is not container not found error.
|
||||
if !dockertools.IsContainerNotFoundError(rmErr) {
|
||||
if !libdocker.IsContainerNotFoundError(rmErr) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -395,12 +396,12 @@ func getSecurityOptSeparator(v *semver.Version) rune {
|
|||
}
|
||||
|
||||
// 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)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !dockertools.IsImageNotFoundError(err) {
|
||||
if !libdocker.IsImageNotFoundError(err) {
|
||||
return fmt.Errorf("failed to inspect sandbox image %q: %v", image, err)
|
||||
}
|
||||
err = client.PullImage(image, dockertypes.AuthConfig{}, dockertypes.ImagePullOptions{})
|
||||
|
@ -437,7 +438,7 @@ type dockerOpt struct {
|
|||
msg string
|
||||
}
|
||||
|
||||
// Expose key/value from dockertools
|
||||
// Expose key/value from dockerOpt.
|
||||
func (d dockerOpt) GetKV() (string, string) {
|
||||
return d.key, d.value
|
||||
}
|
||||
|
|
|
@ -29,8 +29,9 @@ import (
|
|||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
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/kubelet/dockershim/libdocker"
|
||||
)
|
||||
|
||||
func TestLabelsAndAnnotationsRoundTrip(t *testing.T) {
|
||||
|
@ -302,7 +303,7 @@ func TestEnsureSandboxImageExists(t *testing.T) {
|
|||
},
|
||||
"should pull image when it doesn't exist": {
|
||||
injectImage: false,
|
||||
injectErr: dockertools.ImageNotFoundError{ID: "image_id"},
|
||||
injectErr: libdocker.ImageNotFoundError{ID: "image_id"},
|
||||
calls: []string{"inspect_image", "pull"},
|
||||
},
|
||||
"should return error when inspect image fails": {
|
||||
|
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
package libdocker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -77,16 +77,16 @@ type FakeDockerClient struct {
|
|||
}
|
||||
|
||||
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.
|
||||
fakeDockerVersion = "1.8.1"
|
||||
fakeDockerVersion = "1.11.2"
|
||||
|
||||
fakeImageSize = 1024
|
||||
)
|
||||
|
||||
func NewFakeDockerClient() *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),
|
||||
ContainerMap: make(map[string]*dockertypes.ContainerJSON),
|
||||
Clock: clock.RealClock{},
|
||||
|
@ -345,6 +345,14 @@ func (f *FakeDockerClient) AssertImagesPulled(pulled []string) error {
|
|||
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 {
|
||||
sort.StringSlice(expected).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.
|
||||
func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
|
||||
f.Lock()
|
||||
|
@ -434,7 +442,7 @@ func (f *FakeDockerClient) ListContainers(options dockertypes.ContainerListOptio
|
|||
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.
|
||||
func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
|
||||
f.Lock()
|
||||
|
@ -451,7 +459,7 @@ func (f *FakeDockerClient) InspectContainer(id string) (*dockertypes.ContainerJS
|
|||
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.
|
||||
func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageInspect, error) {
|
||||
f.Lock()
|
||||
|
@ -466,7 +474,7 @@ func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageIns
|
|||
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.
|
||||
func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInspect, error) {
|
||||
f.Lock()
|
||||
|
@ -502,7 +510,7 @@ func GetFakeContainerID(name string) string {
|
|||
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.
|
||||
func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
|
||||
f.Lock()
|
||||
|
@ -519,7 +527,7 @@ func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig)
|
|||
timestamp := f.Clock.Now()
|
||||
// The newest container should be in front, because we assume so in GetPodStatus()
|
||||
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.ContainerMap[id] = convertFakeContainer(&FakeContainer{
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (f *FakeDockerClient) StartContainer(id string) error {
|
||||
f.Lock()
|
||||
|
@ -549,12 +557,12 @@ func (f *FakeDockerClient) StartContainer(id string) error {
|
|||
container.State.StartedAt = dockerTimestampToString(timestamp)
|
||||
container.NetworkSettings.IPAddress = "2.3.4.5"
|
||||
f.ContainerMap[id] = container
|
||||
f.updateContainerStatus(id, statusRunningPrefix)
|
||||
f.updateContainerStatus(id, StatusRunningPrefix)
|
||||
f.normalSleep(200, 50, 50)
|
||||
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.
|
||||
func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
|
||||
f.Lock()
|
||||
|
@ -565,7 +573,7 @@ func (f *FakeDockerClient) StopContainer(id string, timeout int) error {
|
|||
}
|
||||
f.appendContainerTrace("Stopped", id)
|
||||
// Container status should be Updated before container moved to ExitedContainerList
|
||||
f.updateContainerStatus(id, statusExitedPrefix)
|
||||
f.updateContainerStatus(id, StatusExitedPrefix)
|
||||
var newList []dockertypes.Container
|
||||
for _, container := range f.RunningContainerList {
|
||||
if container.ID == id {
|
||||
|
@ -615,7 +623,7 @@ func (f *FakeDockerClient) RemoveContainer(id string, opts dockertypes.Container
|
|||
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.
|
||||
func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
|
||||
f.Lock()
|
||||
|
@ -624,7 +632,7 @@ func (f *FakeDockerClient) Logs(id string, opts dockertypes.ContainerLogsOptions
|
|||
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.
|
||||
func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error {
|
||||
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.
|
||||
// Please do not add more functionalities to it.
|
||||
type FakeDockerPuller struct {
|
||||
client DockerInterface
|
||||
client Interface
|
||||
}
|
||||
|
||||
func (f *FakeDockerPuller) Pull(image string, _ []v1.Secret) error {
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
package libdocker
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
@ -23,15 +23,15 @@ import (
|
|||
"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.
|
||||
type instrumentedDockerInterface struct {
|
||||
client DockerInterface
|
||||
type instrumentedInterface struct {
|
||||
client Interface
|
||||
}
|
||||
|
||||
// Creates an instrumented DockerInterface from an existing DockerInterface.
|
||||
func NewInstrumentedDockerInterface(dockerClient DockerInterface) DockerInterface {
|
||||
return instrumentedDockerInterface{
|
||||
// Creates an instrumented Interface from an existing Interface.
|
||||
func NewInstrumentedInterface(dockerClient Interface) Interface {
|
||||
return instrumentedInterface{
|
||||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -62,7 +62,7 @@ func (in instrumentedDockerInterface) ListContainers(options dockertypes.Contain
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -71,7 +71,7 @@ func (in instrumentedDockerInterface) InspectContainer(id string) (*dockertypes.
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -80,7 +80,7 @@ func (in instrumentedDockerInterface) CreateContainer(opts dockertypes.Container
|
|||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) StartContainer(id string) error {
|
||||
func (in instrumentedInterface) StartContainer(id string) error {
|
||||
const operation = "start_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -89,7 +89,7 @@ func (in instrumentedDockerInterface) StartContainer(id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) StopContainer(id string, timeout int) error {
|
||||
func (in instrumentedInterface) StopContainer(id string, timeout int) error {
|
||||
const operation = "stop_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -98,7 +98,7 @@ func (in instrumentedDockerInterface) StopContainer(id string, timeout int) erro
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -107,7 +107,7 @@ func (in instrumentedDockerInterface) RemoveContainer(id string, opts dockertype
|
|||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectImageByRef(image string) (*dockertypes.ImageInspect, error) {
|
||||
func (in instrumentedInterface) InspectImageByRef(image string) (*dockertypes.ImageInspect, error) {
|
||||
const operation = "inspect_image"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -116,7 +116,7 @@ func (in instrumentedDockerInterface) InspectImageByRef(image string) (*dockerty
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -125,7 +125,7 @@ func (in instrumentedDockerInterface) InspectImageByID(image string) (*dockertyp
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -134,7 +134,7 @@ func (in instrumentedDockerInterface) ListImages(opts dockertypes.ImageListOptio
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
err := in.client.PullImage(imageID, auth, opts)
|
||||
|
@ -142,7 +142,7 @@ func (in instrumentedDockerInterface) PullImage(imageID string, auth dockertypes
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -151,7 +151,7 @@ func (in instrumentedDockerInterface) RemoveImage(image string, opts dockertypes
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -160,7 +160,7 @@ func (in instrumentedDockerInterface) Logs(id string, opts dockertypes.Container
|
|||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) Version() (*dockertypes.Version, error) {
|
||||
func (in instrumentedInterface) Version() (*dockertypes.Version, error) {
|
||||
const operation = "version"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -169,7 +169,7 @@ func (in instrumentedDockerInterface) Version() (*dockertypes.Version, error) {
|
|||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) Info() (*dockertypes.Info, error) {
|
||||
func (in instrumentedInterface) Info() (*dockertypes.Info, error) {
|
||||
const operation = "info"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -178,7 +178,7 @@ func (in instrumentedDockerInterface) Info() (*dockertypes.Info, error) {
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -187,7 +187,7 @@ func (in instrumentedDockerInterface) CreateExec(id string, opts dockertypes.Exe
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -196,7 +196,7 @@ func (in instrumentedDockerInterface) StartExec(startExec string, opts dockertyp
|
|||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
|
||||
func (in instrumentedInterface) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
|
||||
const operation = "inspect_exec"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -205,7 +205,7 @@ func (in instrumentedDockerInterface) InspectExec(id string) (*dockertypes.Conta
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -214,7 +214,7 @@ func (in instrumentedDockerInterface) AttachToContainer(id string, opts dockerty
|
|||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
func (in instrumentedInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
const operation = "image_history"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -223,7 +223,7 @@ func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.Ima
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
|
@ -232,7 +232,7 @@ func (in instrumentedDockerInterface) ResizeExecTTY(id string, height, width int
|
|||
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"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
package libdocker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -38,16 +38,16 @@ import (
|
|||
|
||||
// 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.
|
||||
// 2) Wrap the context in this layer to make the DockerInterface cleaner.
|
||||
// 3) Stabilize the DockerInterface. 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
|
||||
// 2) Wrap the context in this layer to make the Interface cleaner.
|
||||
// 3) Stabilize the Interface. The engine-api is still under active development, the interface
|
||||
// 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
|
||||
// DockerInterface to avoid changing DockerInterface as much as possible.
|
||||
// Interface to avoid changing Interface as much as possible.
|
||||
// (See
|
||||
// * https://github.com/docker/engine-api/issues/89
|
||||
// * https://github.com/docker/engine-api/issues/137
|
||||
// * 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.
|
||||
type kubeDockerClient struct {
|
||||
// timeout is the timeout of short running docker operations.
|
||||
|
@ -59,8 +59,8 @@ type kubeDockerClient struct {
|
|||
client *dockerapi.Client
|
||||
}
|
||||
|
||||
// Make sure that kubeDockerClient implemented the DockerInterface.
|
||||
var _ DockerInterface = &kubeDockerClient{}
|
||||
// Make sure that kubeDockerClient implemented the Interface.
|
||||
var _ Interface = &kubeDockerClient{}
|
||||
|
||||
// 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
|
||||
|
@ -83,7 +83,7 @@ const (
|
|||
|
||||
// newKubeDockerClient creates an kubeDockerClient from an existing docker client. If requestTimeout is 0,
|
||||
// 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 {
|
||||
requestTimeout = defaultTimeout
|
||||
}
|
||||
|
@ -576,12 +576,6 @@ func (d *kubeDockerClient) getCustomTimeoutContext(timeout time.Duration) (conte
|
|||
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.
|
||||
func contextError(ctx context.Context) error {
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
package libdocker
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -10,40 +10,23 @@ load(
|
|||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"docker.go",
|
||||
"fake_docker_client.go",
|
||||
"instrumented_docker.go",
|
||||
"kube_docker_client.go",
|
||||
],
|
||||
srcs = ["docker.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1: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/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/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/clock:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"docker_test.go",
|
||||
"kube_docker_client_test.go",
|
||||
],
|
||||
srcs = ["docker_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = [
|
||||
"automanaged",
|
||||
|
@ -51,12 +34,9 @@ go_test(
|
|||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubelet/dockershim/libdocker: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/engine-api/types:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -18,86 +18,22 @@ package dockertools
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dockerdigest "github.com/docker/distribution/digest"
|
||||
dockerref "github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
dockerapi "github.com/docker/engine-api/client"
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"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"
|
||||
)
|
||||
|
||||
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 *not* in use anywhere in the codebase.
|
||||
// TODO: Examine whether we can migrate the unit tests and remove the code.
|
||||
type DockerPuller interface {
|
||||
Pull(image string, secrets []v1.Secret) error
|
||||
GetImageRef(image string) (string, error)
|
||||
|
@ -105,12 +41,12 @@ type DockerPuller interface {
|
|||
|
||||
// dockerPuller is the default implementation of DockerPuller.
|
||||
type dockerPuller struct {
|
||||
client DockerInterface
|
||||
client libdocker.Interface
|
||||
keyring credentialprovider.DockerKeyring
|
||||
}
|
||||
|
||||
// newDockerPuller creates a new instance of the default implementation of DockerPuller.
|
||||
func newDockerPuller(client DockerInterface) DockerPuller {
|
||||
func newDockerPuller(client libdocker.Interface) DockerPuller {
|
||||
return &dockerPuller{
|
||||
client: client,
|
||||
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 {
|
||||
keyring, err := credentialprovider.MakeDockerKeyring(secrets, p.keyring)
|
||||
if err != nil {
|
||||
|
@ -296,123 +134,8 @@ func (p dockerPuller) GetImageRef(image string) (string, error) {
|
|||
}
|
||||
return imageRef, nil
|
||||
}
|
||||
if IsImageNotFoundError(err) {
|
||||
if libdocker.IsImageNotFoundError(err) {
|
||||
return "", nil
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -18,372 +18,17 @@ package dockertools
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"math/rand"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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/credentialprovider"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/images"
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Examine the tests and see if they can be migrated to kuberuntime.
|
||||
func TestPullWithNoSecrets(t *testing.T) {
|
||||
tests := []struct {
|
||||
imageName string
|
||||
|
@ -399,7 +44,7 @@ func TestPullWithNoSecrets(t *testing.T) {
|
|||
}
|
||||
for _, test := range tests {
|
||||
fakeKeyring := &credentialprovider.FakeKeyring{}
|
||||
fakeClient := NewFakeDockerClient()
|
||||
fakeClient := libdocker.NewFakeDockerClient()
|
||||
|
||||
dp := dockerPuller{
|
||||
client: fakeClient,
|
||||
|
@ -412,13 +57,8 @@ func TestPullWithNoSecrets(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
if e, a := 1, len(fakeClient.pulled); e != a {
|
||||
t.Errorf("%s: expected 1 pulled image, got %d: %v", test.imageName, a, fakeClient.pulled)
|
||||
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)
|
||||
if err := fakeClient.AssertImagesPulled([]string{test.imageName}); err != nil {
|
||||
t.Errorf("images pulled do not match the expected: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,7 +82,7 @@ func TestPullWithJSONError(t *testing.T) {
|
|||
}
|
||||
for i, test := range tests {
|
||||
fakeKeyring := &credentialprovider.FakeKeyring{}
|
||||
fakeClient := NewFakeDockerClient()
|
||||
fakeClient := libdocker.NewFakeDockerClient()
|
||||
fakeClient.InjectError("pull", test.err)
|
||||
|
||||
puller := &dockerPuller{
|
||||
|
@ -520,7 +160,7 @@ func TestPullWithSecrets(t *testing.T) {
|
|||
builtInKeyRing := &credentialprovider.BasicDockerKeyring{}
|
||||
builtInKeyRing.Add(test.builtInDockerConfig)
|
||||
|
||||
fakeClient := NewFakeDockerClient()
|
||||
fakeClient := libdocker.NewFakeDockerClient()
|
||||
|
||||
dp := dockerPuller{
|
||||
client: fakeClient,
|
||||
|
@ -532,35 +172,8 @@ func TestPullWithSecrets(t *testing.T) {
|
|||
t.Errorf("%s: unexpected non-nil err: %s", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if e, a := 1, len(fakeClient.pulled); e != a {
|
||||
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)
|
||||
if err := fakeClient.AssertImagesPulledMsgs(test.expectedPulls); err != nil {
|
||||
t.Errorf("images pulled do not match the expected: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ go_library(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//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",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -60,13 +60,13 @@ type nvidiaGPUManager struct {
|
|||
defaultDevices []string
|
||||
// The interface which could get GPU mapping from all the containers.
|
||||
// TODO: Should make this independent of Docker in the future.
|
||||
dockerClient dockertools.DockerInterface
|
||||
dockerClient libdocker.Interface
|
||||
activePodsLister activePodsLister
|
||||
}
|
||||
|
||||
// NewNvidiaGPUManager returns a GPUManager that manages local Nvidia GPUs.
|
||||
// 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 {
|
||||
return nil, fmt.Errorf("invalid docker client specified")
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ import (
|
|||
"k8s.io/kubernetes/pkg/kubelet/config"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
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/eviction"
|
||||
"k8s.io/kubernetes/pkg/kubelet/gpu"
|
||||
|
@ -219,7 +219,7 @@ type KubeletDeps struct {
|
|||
CAdvisorInterface cadvisor.Interface
|
||||
Cloud cloudprovider.Interface
|
||||
ContainerManager cm.ContainerManager
|
||||
DockerClient dockertools.DockerInterface
|
||||
DockerClient libdocker.Interface
|
||||
EventClient v1core.EventsGetter
|
||||
KubeClient clientset.Interface
|
||||
ExternalKubeClient clientgoclientset.Interface
|
||||
|
@ -793,7 +793,7 @@ type Kubelet struct {
|
|||
|
||||
hostname string
|
||||
nodeName types.NodeName
|
||||
dockerClient dockertools.DockerInterface
|
||||
dockerClient libdocker.Interface
|
||||
runtimeCache kubecontainer.RuntimeCache
|
||||
kubeClient clientset.Interface
|
||||
iptClient utilipt.Interface
|
||||
|
|
|
@ -34,7 +34,6 @@ go_library(
|
|||
"//pkg/kubelet/api:go_default_library",
|
||||
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/images:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
|
@ -76,6 +75,7 @@ go_test(
|
|||
"kuberuntime_manager_test.go",
|
||||
"kuberuntime_sandbox_test.go",
|
||||
"labels_test.go",
|
||||
"legacy_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
|
|
|
@ -17,8 +17,10 @@ limitations under the License.
|
|||
package kuberuntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
)
|
||||
|
||||
// This file implements the functions that are needed for backward
|
||||
|
@ -30,12 +32,24 @@ const (
|
|||
// kubelet.containerLogsDir.
|
||||
legacyContainerLogsDir = "/var/log/containers"
|
||||
// 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
|
||||
// logging support.
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -22,7 +22,6 @@ go_library(
|
|||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apis/extensions/validation:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"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
|
||||
// with both the CRI and dockertools comparison logic.
|
||||
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 {
|
||||
result lifecycle.PodAdmitResult
|
||||
}
|
||||
|
@ -42,7 +46,7 @@ var _ lifecycle.PodAdmitHandler = &runtimeAdmitHandler{}
|
|||
// NewRuntimeAdmitHandler returns a sysctlRuntimeAdmitHandler which checks whether
|
||||
// the given runtime support sysctls.
|
||||
func NewRuntimeAdmitHandler(runtime container.Runtime) (*runtimeAdmitHandler, error) {
|
||||
if runtime.Type() == dockertools.DockerType {
|
||||
if runtime.Type() == dockerTypeName {
|
||||
v, err := runtime.APIVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get runtime version: %v", err)
|
||||
|
|
|
@ -27,7 +27,7 @@ go_library(
|
|||
"//pkg/kubelet/cadvisor:go_default_library",
|
||||
"//pkg/kubelet/cm: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/util:go_default_library",
|
||||
"//pkg/util/io:go_default_library",
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
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"
|
||||
kubeio "k8s.io/kubernetes/pkg/util/io"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
@ -52,7 +52,7 @@ func NewHollowKubelet(
|
|||
nodeName string,
|
||||
client *clientset.Clientset,
|
||||
cadvisorInterface cadvisor.Interface,
|
||||
dockerClient dockertools.DockerInterface,
|
||||
dockerClient libdocker.Interface,
|
||||
kubeletPort, kubeletReadOnlyPort int,
|
||||
containerManager cm.ContainerManager,
|
||||
maxPods int, podsPerCore int,
|
||||
|
|
|
@ -101,7 +101,7 @@ go_test(
|
|||
"//pkg/kubelet/api/v1alpha1/stats:go_default_library",
|
||||
"//pkg/kubelet/cm: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/metrics:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/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"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
@ -258,16 +258,16 @@ func containerGCTest(f *framework.Framework, test testRun) {
|
|||
|
||||
// Runs containerGCTest using the docker runtime.
|
||||
func dockerContainerGCTest(f *framework.Framework, test testRun) {
|
||||
var runtime docker.DockerInterface
|
||||
var runtime libdocker.Interface
|
||||
BeforeEach(func() {
|
||||
runtime = docker.ConnectToDockerOrDie(defaultDockerEndpoint, defaultRuntimeRequestTimeoutDuration, defaultImagePullProgressDeadline)
|
||||
runtime = libdocker.ConnectToDockerOrDie(defaultDockerEndpoint, defaultRuntimeRequestTimeoutDuration, defaultImagePullProgressDeadline)
|
||||
})
|
||||
for _, pod := range test.testPods {
|
||||
// Initialize the getContainerNames function to use the dockertools api
|
||||
thisPrefix := pod.containerPrefix
|
||||
pod.getContainerNames = func() ([]string, error) {
|
||||
relevantContainers := []string{}
|
||||
dockerContainers, err := docker.GetKubeletDockerContainers(runtime, true)
|
||||
dockerContainers, err := libdocker.GetKubeletDockerContainers(runtime, true)
|
||||
if err != nil {
|
||||
return relevantContainers, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue