forked from barak/tarpoon
Add glide.yaml and vendor deps
This commit is contained in:
parent
db918f12ad
commit
5b3d5e81bd
18880 changed files with 5166045 additions and 1 deletions
139
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/BUILD
generated
vendored
Normal file
139
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"container_gc.go",
|
||||
"convert.go",
|
||||
"docker.go",
|
||||
"docker_manager.go",
|
||||
"docker_manager_linux.go",
|
||||
"exec.go",
|
||||
"fake_docker_client.go",
|
||||
"fake_manager.go",
|
||||
"images.go",
|
||||
"instrumented_docker.go",
|
||||
"kube_docker_client.go",
|
||||
"labels.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/custommetrics:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/images:go_default_library",
|
||||
"//pkg/kubelet/leaky:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
"//pkg/kubelet/metrics:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/hairpin:go_default_library",
|
||||
"//pkg/kubelet/prober/results:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/cache:go_default_library",
|
||||
"//pkg/kubelet/util/format:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/schema:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/oom:go_default_library",
|
||||
"//pkg/util/procfs:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/selinux:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//vendor:github.com/docker/distribution/digest",
|
||||
"//vendor:github.com/docker/distribution/reference",
|
||||
"//vendor:github.com/docker/docker/pkg/jsonmessage",
|
||||
"//vendor:github.com/docker/docker/pkg/stdcopy",
|
||||
"//vendor:github.com/docker/engine-api/client",
|
||||
"//vendor:github.com/docker/engine-api/types",
|
||||
"//vendor:github.com/docker/engine-api/types/container",
|
||||
"//vendor:github.com/docker/engine-api/types/strslice",
|
||||
"//vendor:github.com/docker/engine-api/types/versions",
|
||||
"//vendor:github.com/docker/go-connections/nat",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:golang.org/x/net/context",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"container_gc_test.go",
|
||||
"convert_test.go",
|
||||
"docker_manager_test.go",
|
||||
"docker_test.go",
|
||||
"images_test.go",
|
||||
"labels_test.go",
|
||||
],
|
||||
data = [
|
||||
"fixtures/seccomp/sub/subtest",
|
||||
"fixtures/seccomp/test",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = [
|
||||
"automanaged",
|
||||
"skip",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/images:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/mock_network:go_default_library",
|
||||
"//pkg/kubelet/network/testing:go_default_library",
|
||||
"//pkg/kubelet/prober/results:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/format:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/hash:go_default_library",
|
||||
"//pkg/util/intstr:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//vendor:github.com/docker/docker/pkg/jsonmessage",
|
||||
"//vendor:github.com/docker/engine-api/types",
|
||||
"//vendor:github.com/docker/engine-api/types/container",
|
||||
"//vendor:github.com/docker/engine-api/types/strslice",
|
||||
"//vendor:github.com/docker/go-connections/nat",
|
||||
"//vendor:github.com/golang/mock/gomock",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
],
|
||||
)
|
||||
283
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/container_gc.go
generated
vendored
Normal file
283
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/container_gc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
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 dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/golang/glog"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type containerGC struct {
|
||||
client DockerInterface
|
||||
podGetter podGetter
|
||||
containerLogsDir string
|
||||
}
|
||||
|
||||
func NewContainerGC(client DockerInterface, podGetter podGetter, containerLogsDir string) *containerGC {
|
||||
return &containerGC{
|
||||
client: client,
|
||||
podGetter: podGetter,
|
||||
containerLogsDir: containerLogsDir,
|
||||
}
|
||||
}
|
||||
|
||||
// Internal information kept for containers being considered for GC.
|
||||
type containerGCInfo struct {
|
||||
// Docker ID of the container.
|
||||
id string
|
||||
|
||||
// Docker name of the container.
|
||||
name string
|
||||
|
||||
// Creation time for the container.
|
||||
createTime time.Time
|
||||
|
||||
// Full pod name, including namespace in the format `namespace_podName`.
|
||||
// This comes from dockertools.ParseDockerName(...)
|
||||
podNameWithNamespace string
|
||||
|
||||
// Container name in pod
|
||||
containerName string
|
||||
}
|
||||
|
||||
// Containers are considered for eviction as units of (UID, container name) pair.
|
||||
type evictUnit struct {
|
||||
// UID of the pod.
|
||||
uid types.UID
|
||||
|
||||
// Name of the container in the pod.
|
||||
name string
|
||||
}
|
||||
|
||||
type containersByEvictUnit map[evictUnit][]containerGCInfo
|
||||
|
||||
// Returns the number of containers in this map.
|
||||
func (cu containersByEvictUnit) NumContainers() int {
|
||||
num := 0
|
||||
for key := range cu {
|
||||
num += len(cu[key])
|
||||
}
|
||||
|
||||
return num
|
||||
}
|
||||
|
||||
// Returns the number of pod in this map.
|
||||
func (cu containersByEvictUnit) NumEvictUnits() int {
|
||||
return len(cu)
|
||||
}
|
||||
|
||||
// Newest first.
|
||||
type byCreated []containerGCInfo
|
||||
|
||||
func (a byCreated) Len() int { return len(a) }
|
||||
func (a byCreated) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byCreated) Less(i, j int) bool { return a[i].createTime.After(a[j].createTime) }
|
||||
|
||||
func (cgc *containerGC) enforceMaxContainersPerEvictUnit(evictUnits containersByEvictUnit, MaxContainers int) {
|
||||
for uid := range evictUnits {
|
||||
toRemove := len(evictUnits[uid]) - MaxContainers
|
||||
|
||||
if toRemove > 0 {
|
||||
evictUnits[uid] = cgc.removeOldestN(evictUnits[uid], toRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the oldest toRemove containers and returns the resulting slice.
|
||||
func (cgc *containerGC) removeOldestN(containers []containerGCInfo, toRemove int) []containerGCInfo {
|
||||
// Remove from oldest to newest (last to first).
|
||||
numToKeep := len(containers) - toRemove
|
||||
for i := numToKeep; i < len(containers); i++ {
|
||||
cgc.removeContainer(containers[i].id, containers[i].podNameWithNamespace, containers[i].containerName)
|
||||
}
|
||||
|
||||
// Assume we removed the containers so that we're not too aggressive.
|
||||
return containers[:numToKeep]
|
||||
}
|
||||
|
||||
// Get all containers that are evictable. Evictable containers are: not running
|
||||
// and created more than MinAge ago.
|
||||
func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByEvictUnit, []containerGCInfo, error) {
|
||||
containers, err := GetKubeletDockerContainers(cgc.client, true)
|
||||
if err != nil {
|
||||
return containersByEvictUnit{}, []containerGCInfo{}, err
|
||||
}
|
||||
|
||||
unidentifiedContainers := make([]containerGCInfo, 0)
|
||||
evictUnits := make(containersByEvictUnit)
|
||||
newestGCTime := time.Now().Add(-minAge)
|
||||
for _, container := range containers {
|
||||
// Prune out running containers.
|
||||
data, err := cgc.client.InspectContainer(container.ID)
|
||||
if err != nil {
|
||||
// Container may have been removed already, skip.
|
||||
continue
|
||||
} else if data.State.Running {
|
||||
continue
|
||||
}
|
||||
|
||||
created, err := ParseDockerTimestamp(data.Created)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse Created timestamp %q for container %q", data.Created, container.ID)
|
||||
}
|
||||
if newestGCTime.Before(created) {
|
||||
continue
|
||||
}
|
||||
|
||||
containerInfo := containerGCInfo{
|
||||
id: container.ID,
|
||||
name: container.Names[0],
|
||||
createTime: created,
|
||||
}
|
||||
|
||||
containerName, _, err := ParseDockerName(container.Names[0])
|
||||
|
||||
if err != nil {
|
||||
unidentifiedContainers = append(unidentifiedContainers, containerInfo)
|
||||
} else {
|
||||
key := evictUnit{
|
||||
uid: containerName.PodUID,
|
||||
name: containerName.ContainerName,
|
||||
}
|
||||
containerInfo.podNameWithNamespace = containerName.PodFullName
|
||||
containerInfo.containerName = containerName.ContainerName
|
||||
evictUnits[key] = append(evictUnits[key], containerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the containers by age.
|
||||
for uid := range evictUnits {
|
||||
sort.Sort(byCreated(evictUnits[uid]))
|
||||
}
|
||||
|
||||
return evictUnits, unidentifiedContainers, nil
|
||||
}
|
||||
|
||||
// GarbageCollect removes dead containers using the specified container gc policy
|
||||
func (cgc *containerGC) GarbageCollect(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool) error {
|
||||
// Separate containers by evict units.
|
||||
evictUnits, unidentifiedContainers, err := cgc.evictableContainers(gcPolicy.MinAge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove unidentified containers.
|
||||
for _, container := range unidentifiedContainers {
|
||||
glog.Infof("Removing unidentified dead container %q with ID %q", container.name, container.id)
|
||||
err = cgc.client.RemoveContainer(container.id, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove unidentified dead container %q: %v", container.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deleted pod containers if all sources are ready.
|
||||
if allSourcesReady {
|
||||
for key, unit := range evictUnits {
|
||||
if cgc.isPodDeleted(key.uid) {
|
||||
cgc.removeOldestN(unit, len(unit)) // Remove all.
|
||||
delete(evictUnits, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce max containers per evict unit.
|
||||
if gcPolicy.MaxPerPodContainer >= 0 {
|
||||
cgc.enforceMaxContainersPerEvictUnit(evictUnits, gcPolicy.MaxPerPodContainer)
|
||||
}
|
||||
|
||||
// Enforce max total number of containers.
|
||||
if gcPolicy.MaxContainers >= 0 && evictUnits.NumContainers() > gcPolicy.MaxContainers {
|
||||
// Leave an equal number of containers per evict unit (min: 1).
|
||||
numContainersPerEvictUnit := gcPolicy.MaxContainers / evictUnits.NumEvictUnits()
|
||||
if numContainersPerEvictUnit < 1 {
|
||||
numContainersPerEvictUnit = 1
|
||||
}
|
||||
cgc.enforceMaxContainersPerEvictUnit(evictUnits, numContainersPerEvictUnit)
|
||||
|
||||
// If we still need to evict, evict oldest first.
|
||||
numContainers := evictUnits.NumContainers()
|
||||
if numContainers > gcPolicy.MaxContainers {
|
||||
flattened := make([]containerGCInfo, 0, numContainers)
|
||||
for uid := range evictUnits {
|
||||
flattened = append(flattened, evictUnits[uid]...)
|
||||
}
|
||||
sort.Sort(byCreated(flattened))
|
||||
|
||||
cgc.removeOldestN(flattened, numContainers-gcPolicy.MaxContainers)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead symlinks - should only happen on upgrade
|
||||
// from a k8s version without proper log symlink cleanup
|
||||
logSymlinks, _ := filepath.Glob(path.Join(cgc.containerLogsDir, fmt.Sprintf("*.%s", LogSuffix)))
|
||||
for _, logSymlink := range logSymlinks {
|
||||
if _, err = os.Stat(logSymlink); os.IsNotExist(err) {
|
||||
err = os.Remove(logSymlink)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove container log dead symlink %q: %v", logSymlink, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cgc *containerGC) removeContainer(id string, podNameWithNamespace string, containerName string) {
|
||||
glog.V(4).Infof("Removing container %q name %q", id, containerName)
|
||||
err := cgc.client.RemoveContainer(id, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove container %q: %v", id, err)
|
||||
}
|
||||
symlinkPath := LogSymlink(cgc.containerLogsDir, podNameWithNamespace, containerName, id)
|
||||
err = os.Remove(symlinkPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
glog.Warningf("Failed to remove container %q log symlink %q: %v", id, symlinkPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cgc *containerGC) deleteContainer(id string) error {
|
||||
containerInfo, err := cgc.client.InspectContainer(id)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to inspect container %q: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if containerInfo.State.Running {
|
||||
return fmt.Errorf("container %q is still running", id)
|
||||
}
|
||||
|
||||
containerName, _, err := ParseDockerName(containerInfo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cgc.removeContainer(id, containerName.PodFullName, containerName.ContainerName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cgc *containerGC) isPodDeleted(podUID types.UID) bool {
|
||||
_, found := cgc.podGetter.GetPodByUID(podUID)
|
||||
return !found
|
||||
}
|
||||
269
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/container_gc_test.go
generated
vendored
Normal file
269
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/container_gc_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
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 dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
func newTestContainerGC(t *testing.T) (*containerGC, *FakeDockerClient) {
|
||||
fakeDocker := new(FakeDockerClient)
|
||||
fakePodGetter := newFakePodGetter()
|
||||
gc := NewContainerGC(fakeDocker, fakePodGetter, "")
|
||||
return gc, fakeDocker
|
||||
}
|
||||
|
||||
// Makes a stable time object, lower id is earlier time.
|
||||
func makeTime(id int) time.Time {
|
||||
var zero time.Time
|
||||
return zero.Add(time.Duration(id) * time.Second)
|
||||
}
|
||||
|
||||
// Makes a container with the specified properties.
|
||||
func makeContainer(id, uid, name string, running bool, created time.Time) *FakeContainer {
|
||||
return &FakeContainer{
|
||||
Name: fmt.Sprintf("/k8s_%s_bar_new_%s_42", name, uid),
|
||||
Running: running,
|
||||
ID: id,
|
||||
CreatedAt: created,
|
||||
}
|
||||
}
|
||||
|
||||
// Makes a container with unidentified name and specified properties.
|
||||
func makeUndefinedContainer(id string, running bool, created time.Time) *FakeContainer {
|
||||
return &FakeContainer{
|
||||
Name: "/k8s_unidentified",
|
||||
Running: running,
|
||||
ID: id,
|
||||
CreatedAt: created,
|
||||
}
|
||||
}
|
||||
|
||||
func addPods(podGetter podGetter, podUIDs ...types.UID) {
|
||||
fakePodGetter := podGetter.(*fakePodGetter)
|
||||
for _, uid := range podUIDs {
|
||||
fakePodGetter.pods[uid] = &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "pod" + string(uid),
|
||||
Namespace: "test",
|
||||
UID: uid,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyStringArrayEqualsAnyOrder(t *testing.T, actual, expected []string) {
|
||||
act := make([]string, len(actual))
|
||||
exp := make([]string, len(expected))
|
||||
copy(act, actual)
|
||||
copy(exp, expected)
|
||||
|
||||
sort.StringSlice(act).Sort()
|
||||
sort.StringSlice(exp).Sort()
|
||||
|
||||
if !reflect.DeepEqual(exp, act) {
|
||||
t.Errorf("Expected(sorted): %#v, Actual(sorted): %#v", exp, act)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteContainerSkipRunningContainer(t *testing.T) {
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers([]*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", true, makeTime(0)),
|
||||
})
|
||||
addPods(gc.podGetter, "foo")
|
||||
|
||||
assert.Error(t, gc.deleteContainer("1876"))
|
||||
assert.Len(t, fakeDocker.Removed, 0)
|
||||
}
|
||||
|
||||
func TestDeleteContainerRemoveDeadContainer(t *testing.T) {
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers([]*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
})
|
||||
addPods(gc.podGetter, "foo")
|
||||
|
||||
assert.Nil(t, gc.deleteContainer("1876"))
|
||||
assert.Len(t, fakeDocker.Removed, 1)
|
||||
}
|
||||
|
||||
func TestGarbageCollectZeroMaxContainers(t *testing.T) {
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers([]*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
})
|
||||
addPods(gc.podGetter, "foo")
|
||||
|
||||
assert.Nil(t, gc.GarbageCollect(kubecontainer.ContainerGCPolicy{MinAge: time.Minute, MaxPerPodContainer: 1, MaxContainers: 0}, true))
|
||||
assert.Len(t, fakeDocker.Removed, 1)
|
||||
}
|
||||
|
||||
func TestGarbageCollectNoMaxPerPodContainerLimit(t *testing.T) {
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers([]*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
makeContainer("2876", "foo1", "POD", false, makeTime(1)),
|
||||
makeContainer("3876", "foo2", "POD", false, makeTime(2)),
|
||||
makeContainer("4876", "foo3", "POD", false, makeTime(3)),
|
||||
makeContainer("5876", "foo4", "POD", false, makeTime(4)),
|
||||
})
|
||||
addPods(gc.podGetter, "foo", "foo1", "foo2", "foo3", "foo4")
|
||||
|
||||
assert.Nil(t, gc.GarbageCollect(kubecontainer.ContainerGCPolicy{MinAge: time.Minute, MaxPerPodContainer: -1, MaxContainers: 4}, true))
|
||||
assert.Len(t, fakeDocker.Removed, 1)
|
||||
}
|
||||
|
||||
func TestGarbageCollectNoMaxLimit(t *testing.T) {
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers([]*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
makeContainer("2876", "foo1", "POD", false, makeTime(0)),
|
||||
makeContainer("3876", "foo2", "POD", false, makeTime(0)),
|
||||
makeContainer("4876", "foo3", "POD", false, makeTime(0)),
|
||||
makeContainer("5876", "foo4", "POD", false, makeTime(0)),
|
||||
})
|
||||
addPods(gc.podGetter, "foo", "foo1", "foo2", "foo3", "foo4")
|
||||
|
||||
assert.Nil(t, gc.GarbageCollect(kubecontainer.ContainerGCPolicy{MinAge: time.Minute, MaxPerPodContainer: -1, MaxContainers: -1}, true))
|
||||
assert.Len(t, fakeDocker.Removed, 0)
|
||||
}
|
||||
|
||||
func TestGarbageCollect(t *testing.T) {
|
||||
tests := []struct {
|
||||
containers []*FakeContainer
|
||||
expectedRemoved []string
|
||||
}{
|
||||
// Don't remove containers started recently.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, time.Now()),
|
||||
makeContainer("2876", "foo", "POD", false, time.Now()),
|
||||
makeContainer("3876", "foo", "POD", false, time.Now()),
|
||||
},
|
||||
},
|
||||
// Remove oldest containers.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("3876", "foo", "POD", false, makeTime(2)),
|
||||
},
|
||||
expectedRemoved: []string{"1876"},
|
||||
},
|
||||
// Only remove non-running containers.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", true, makeTime(0)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("3876", "foo", "POD", false, makeTime(2)),
|
||||
makeContainer("4876", "foo", "POD", false, makeTime(3)),
|
||||
},
|
||||
expectedRemoved: []string{"2876"},
|
||||
},
|
||||
// Less than maxContainerCount doesn't delete any.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
},
|
||||
},
|
||||
// maxContainerCount applies per (UID,container) pair.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("3876", "foo", "POD", false, makeTime(2)),
|
||||
makeContainer("1076", "foo", "bar", false, makeTime(0)),
|
||||
makeContainer("2076", "foo", "bar", false, makeTime(1)),
|
||||
makeContainer("3076", "foo", "bar", false, makeTime(2)),
|
||||
makeContainer("1176", "foo2", "POD", false, makeTime(0)),
|
||||
makeContainer("2176", "foo2", "POD", false, makeTime(1)),
|
||||
makeContainer("3176", "foo2", "POD", false, makeTime(2)),
|
||||
},
|
||||
expectedRemoved: []string{"1076", "1176", "1876"},
|
||||
},
|
||||
// Remove non-running unidentified Kubernetes containers.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeUndefinedContainer("1876", true, makeTime(0)),
|
||||
makeUndefinedContainer("2876", false, makeTime(0)),
|
||||
makeContainer("3876", "foo", "POD", false, makeTime(0)),
|
||||
},
|
||||
expectedRemoved: []string{"2876"},
|
||||
},
|
||||
// Max limit applied and tries to keep from every pod.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(0)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("3876", "foo1", "POD", false, makeTime(0)),
|
||||
makeContainer("4876", "foo1", "POD", false, makeTime(1)),
|
||||
makeContainer("5876", "foo2", "POD", false, makeTime(0)),
|
||||
makeContainer("6876", "foo2", "POD", false, makeTime(1)),
|
||||
makeContainer("7876", "foo3", "POD", false, makeTime(0)),
|
||||
makeContainer("8876", "foo3", "POD", false, makeTime(1)),
|
||||
makeContainer("9876", "foo4", "POD", false, makeTime(0)),
|
||||
makeContainer("10876", "foo4", "POD", false, makeTime(1)),
|
||||
},
|
||||
expectedRemoved: []string{"1876", "3876", "5876", "7876", "9876"},
|
||||
},
|
||||
// If more pods than limit allows, evicts oldest pod.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(2)),
|
||||
makeContainer("3876", "foo1", "POD", false, makeTime(1)),
|
||||
makeContainer("4876", "foo1", "POD", false, makeTime(2)),
|
||||
makeContainer("5876", "foo2", "POD", false, makeTime(0)),
|
||||
makeContainer("6876", "foo3", "POD", false, makeTime(1)),
|
||||
makeContainer("7876", "foo4", "POD", false, makeTime(0)),
|
||||
makeContainer("8876", "foo5", "POD", false, makeTime(1)),
|
||||
makeContainer("9876", "foo6", "POD", false, makeTime(2)),
|
||||
makeContainer("10876", "foo7", "POD", false, makeTime(1)),
|
||||
},
|
||||
expectedRemoved: []string{"1876", "3876", "5876", "7876"},
|
||||
},
|
||||
// Containers for deleted pods should be GC'd.
|
||||
{
|
||||
containers: []*FakeContainer{
|
||||
makeContainer("1876", "foo", "POD", false, makeTime(1)),
|
||||
makeContainer("2876", "foo", "POD", false, makeTime(2)),
|
||||
makeContainer("3876", "deleted", "POD", false, makeTime(1)),
|
||||
makeContainer("4876", "deleted", "POD", false, makeTime(2)),
|
||||
makeContainer("5876", "deleted", "POD", false, time.Now()), // Deleted pods still respect MinAge.
|
||||
},
|
||||
expectedRemoved: []string{"3876", "4876"},
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
t.Logf("Running test case with index %d", i)
|
||||
gc, fakeDocker := newTestContainerGC(t)
|
||||
fakeDocker.SetFakeContainers(test.containers)
|
||||
addPods(gc.podGetter, "foo", "foo1", "foo2", "foo3", "foo4", "foo5", "foo6", "foo7")
|
||||
assert.Nil(t, gc.GarbageCollect(kubecontainer.ContainerGCPolicy{MinAge: time.Hour, MaxPerPodContainer: 2, MaxContainers: 6}, true))
|
||||
verifyStringArrayEqualsAnyOrder(t, fakeDocker.Removed, test.expectedRemoved)
|
||||
}
|
||||
}
|
||||
84
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/convert.go
generated
vendored
Normal file
84
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/convert.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
// This file contains helper functions to convert docker API types to runtime
|
||||
// (kubecontainer) types.
|
||||
const (
|
||||
statusRunningPrefix = "Up"
|
||||
statusExitedPrefix = "Exited"
|
||||
)
|
||||
|
||||
func mapState(state string) kubecontainer.ContainerState {
|
||||
// Parse the state string in dockertypes.Container. This could break when
|
||||
// we upgrade docker.
|
||||
switch {
|
||||
case strings.HasPrefix(state, statusRunningPrefix):
|
||||
return kubecontainer.ContainerStateRunning
|
||||
case strings.HasPrefix(state, statusExitedPrefix):
|
||||
return kubecontainer.ContainerStateExited
|
||||
default:
|
||||
return kubecontainer.ContainerStateUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// Converts dockertypes.Container to kubecontainer.Container.
|
||||
func toRuntimeContainer(c *dockertypes.Container) (*kubecontainer.Container, error) {
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("unable to convert a nil pointer to a runtime container")
|
||||
}
|
||||
|
||||
dockerName, hash, err := getDockerContainerNameInfo(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &kubecontainer.Container{
|
||||
ID: kubecontainer.DockerID(c.ID).ContainerID(),
|
||||
Name: dockerName.ContainerName,
|
||||
Image: c.Image,
|
||||
ImageID: c.ImageID,
|
||||
Hash: hash,
|
||||
// (random-liu) docker uses status to indicate whether a container is running or exited.
|
||||
// However, in kubernetes we usually use state to indicate whether a container is running or exited,
|
||||
// while use status to indicate the comprehensive status of the container. So we have different naming
|
||||
// norm here.
|
||||
State: mapState(c.Status),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Converts dockertypes.Image to kubecontainer.Image.
|
||||
func toRuntimeImage(image *dockertypes.Image) (*kubecontainer.Image, error) {
|
||||
if image == nil {
|
||||
return nil, fmt.Errorf("unable to convert a nil pointer to a runtime image")
|
||||
}
|
||||
|
||||
return &kubecontainer.Image{
|
||||
ID: image.ID,
|
||||
RepoTags: image.RepoTags,
|
||||
RepoDigests: image.RepoDigests,
|
||||
Size: image.VirtualSize,
|
||||
}, nil
|
||||
}
|
||||
90
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/convert_test.go
generated
vendored
Normal file
90
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/convert_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
func TestMapState(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected kubecontainer.ContainerState
|
||||
}{
|
||||
{input: "Up 5 hours", expected: kubecontainer.ContainerStateRunning},
|
||||
{input: "Exited (0) 2 hours ago", expected: kubecontainer.ContainerStateExited},
|
||||
{input: "Created", expected: kubecontainer.ContainerStateUnknown},
|
||||
{input: "Random string", expected: kubecontainer.ContainerStateUnknown},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
if actual := mapState(test.input); actual != test.expected {
|
||||
t.Errorf("Test[%d]: expected %q, got %q", i, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToRuntimeContainer(t *testing.T) {
|
||||
original := &dockertypes.Container{
|
||||
ID: "ab2cdf",
|
||||
Image: "bar_image",
|
||||
Names: []string{"/k8s_bar.5678_foo_ns_1234_42"},
|
||||
Status: "Up 5 hours",
|
||||
}
|
||||
expected := &kubecontainer.Container{
|
||||
ID: kubecontainer.ContainerID{Type: "docker", ID: "ab2cdf"},
|
||||
Name: "bar",
|
||||
Image: "bar_image",
|
||||
Hash: 0x5678,
|
||||
State: kubecontainer.ContainerStateRunning,
|
||||
}
|
||||
|
||||
actual, err := toRuntimeContainer(original)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("expected %#v, got %#v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToRuntimeImage(t *testing.T) {
|
||||
original := &dockertypes.Image{
|
||||
ID: "aeeea",
|
||||
RepoTags: []string{"abc", "def"},
|
||||
RepoDigests: []string{"123", "456"},
|
||||
VirtualSize: 1234,
|
||||
}
|
||||
expected := &kubecontainer.Image{
|
||||
ID: "aeeea",
|
||||
RepoTags: []string{"abc", "def"},
|
||||
RepoDigests: []string{"123", "456"},
|
||||
Size: 1234,
|
||||
}
|
||||
|
||||
actual, err := toRuntimeImage(original)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("expected %#v, got %#v", expected, actual)
|
||||
}
|
||||
}
|
||||
402
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker.go
generated
vendored
Normal file
402
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker.go
generated
vendored
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
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 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/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/images"
|
||||
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
PodInfraContainerName = leaky.PodInfraContainerName
|
||||
DockerPrefix = "docker://"
|
||||
DockerPullablePrefix = "docker-pullable://"
|
||||
LogSuffix = "log"
|
||||
ext4MaxFileNameLen = 255
|
||||
)
|
||||
|
||||
// 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.
|
||||
type DockerPuller interface {
|
||||
Pull(image string, secrets []v1.Secret) error
|
||||
IsImagePresent(image string) (bool, error)
|
||||
}
|
||||
|
||||
// dockerPuller is the default implementation of DockerPuller.
|
||||
type dockerPuller struct {
|
||||
client DockerInterface
|
||||
keyring credentialprovider.DockerKeyring
|
||||
}
|
||||
|
||||
// newDockerPuller creates a new instance of the default implementation of DockerPuller.
|
||||
func newDockerPuller(client DockerInterface) DockerPuller {
|
||||
return &dockerPuller{
|
||||
client: client,
|
||||
keyring: credentialprovider.NewDockerKeyring(),
|
||||
}
|
||||
}
|
||||
|
||||
func filterHTTPError(err error, image string) error {
|
||||
// docker/docker/pull/11314 prints detailed error info for docker pull.
|
||||
// When it hits 502, it returns a verbose html output including an inline svg,
|
||||
// which makes the output of kubectl get pods much harder to parse.
|
||||
// Here converts such verbose output to a concise one.
|
||||
jerr, ok := err.(*jsonmessage.JSONError)
|
||||
if ok && (jerr.Code == http.StatusBadGateway ||
|
||||
jerr.Code == http.StatusServiceUnavailable ||
|
||||
jerr.Code == http.StatusGatewayTimeout) {
|
||||
glog.V(2).Infof("Pulling image %q failed: %v", image, err)
|
||||
return images.RegistryUnavailable
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// The only used image pull option RegistryAuth will be set in kube_docker_client
|
||||
opts := dockertypes.ImagePullOptions{}
|
||||
|
||||
creds, haveCredentials := keyring.Lookup(image)
|
||||
if !haveCredentials {
|
||||
glog.V(1).Infof("Pulling image %s without credentials", image)
|
||||
|
||||
err := p.client.PullImage(image, dockertypes.AuthConfig{}, opts)
|
||||
if err == nil {
|
||||
// Sometimes PullImage failed with no error returned.
|
||||
exist, ierr := p.IsImagePresent(image)
|
||||
if ierr != nil {
|
||||
glog.Warningf("Failed to inspect image %s: %v", image, ierr)
|
||||
}
|
||||
if !exist {
|
||||
return fmt.Errorf("image pull failed for unknown error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Image spec: [<registry>/]<repository>/<image>[:<version] so we count '/'
|
||||
explicitRegistry := (strings.Count(image, "/") == 2)
|
||||
// Hack, look for a private registry, and decorate the error with the lack of
|
||||
// credentials. This is heuristic, and really probably could be done better
|
||||
// by talking to the registry API directly from the kubelet here.
|
||||
if explicitRegistry {
|
||||
return fmt.Errorf("image pull failed for %s, this may be because there are no credentials on this request. details: (%v)", image, err)
|
||||
}
|
||||
|
||||
return filterHTTPError(err, image)
|
||||
}
|
||||
|
||||
var pullErrs []error
|
||||
for _, currentCreds := range creds {
|
||||
err = p.client.PullImage(image, credentialprovider.LazyProvide(currentCreds), opts)
|
||||
// If there was no error, return success
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pullErrs = append(pullErrs, filterHTTPError(err, image))
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(pullErrs)
|
||||
}
|
||||
|
||||
func (p dockerPuller) IsImagePresent(image string) (bool, error) {
|
||||
_, err := p.client.InspectImageByRef(image)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if _, ok := err.(imageNotFoundError); ok {
|
||||
return false, nil
|
||||
}
|
||||
return false, 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.HashContainer(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 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)
|
||||
}
|
||||
|
||||
// GetKubeletDockerContainers lists all container or just the running ones.
|
||||
// Returns a list of docker containers that we manage
|
||||
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
|
||||
}
|
||||
2694
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager.go
generated
vendored
Normal file
2694
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
63
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_linux.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
func getContainerIP(container *dockertypes.ContainerJSON) string {
|
||||
result := ""
|
||||
if container.NetworkSettings != nil {
|
||||
result = container.NetworkSettings.IPAddress
|
||||
|
||||
// Fall back to IPv6 address if no IPv4 address is present
|
||||
if result == "" {
|
||||
result = container.NetworkSettings.GlobalIPv6Address
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// We don't want to override the networking mode on Linux.
|
||||
func getNetworkingMode() string { return "" }
|
||||
|
||||
// Returns true if the container name matches the infrastructure's container name
|
||||
func containerProvidesPodIP(name *KubeletContainerName) bool {
|
||||
return name.ContainerName == PodInfraContainerName
|
||||
}
|
||||
|
||||
// Returns Seccomp and AppArmor Security options
|
||||
func (dm *DockerManager) getSecurityOpts(pod *v1.Pod, ctrName string) ([]dockerOpt, error) {
|
||||
var securityOpts []dockerOpt
|
||||
if seccompOpts, err := dm.getSeccompOpts(pod, ctrName); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
securityOpts = append(securityOpts, seccompOpts...)
|
||||
}
|
||||
|
||||
if appArmorOpts, err := dm.getAppArmorOpts(pod, ctrName); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
securityOpts = append(securityOpts, appArmorOpts...)
|
||||
}
|
||||
|
||||
return securityOpts, nil
|
||||
}
|
||||
2402
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_test.go
generated
vendored
Normal file
2402
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_unsupported.go
generated
vendored
Normal file
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// +build !linux,!windows
|
||||
|
||||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
func getContainerIP(container *dockertypes.ContainerJSON) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getNetworkingMode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func containerProvidesPodIP(name *KubeletContainerName) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns nil as both Seccomp and AppArmor security options are not valid on Windows
|
||||
func (dm *DockerManager) getSecurityOpts(pod *v1.Pod, ctrName string) ([]dockerOpt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
58
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_windows.go
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_manager_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
func getContainerIP(container *dockertypes.ContainerJSON) string {
|
||||
if container.NetworkSettings != nil {
|
||||
for _, network := range container.NetworkSettings.Networks {
|
||||
if network.IPAddress != "" {
|
||||
return network.IPAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getNetworkingMode() string {
|
||||
// Allow override via env variable. Otherwise, use a default "kubenet" network
|
||||
netMode := os.Getenv("CONTAINER_NETWORK")
|
||||
if netMode == "" {
|
||||
netMode = "kubenet"
|
||||
}
|
||||
return netMode
|
||||
}
|
||||
|
||||
// Infrastructure containers are not supported on Windows. For this reason, we
|
||||
// make sure to not grab the infra container's IP for the pod.
|
||||
func containerProvidesPodIP(name *KubeletContainerName) bool {
|
||||
return name.ContainerName != PodInfraContainerName
|
||||
}
|
||||
|
||||
// Returns nil as both Seccomp and AppArmor security options are not valid on Windows
|
||||
func (dm *DockerManager) getSecurityOpts(pod *v1.Pod, ctrName string) ([]dockerOpt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
1004
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_test.go
generated
vendored
Normal file
1004
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/docker_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
168
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/exec.go
generated
vendored
Normal file
168
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/exec.go
generated
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/golang/glog"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/term"
|
||||
)
|
||||
|
||||
// ExecHandler knows how to execute a command in a running Docker container.
|
||||
type ExecHandler interface {
|
||||
ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size, timeout time.Duration) error
|
||||
}
|
||||
|
||||
// NsenterExecHandler executes commands in Docker containers using nsenter.
|
||||
type NsenterExecHandler struct{}
|
||||
|
||||
// TODO should we support nsenter in a container, running with elevated privs and --pid=host?
|
||||
func (*NsenterExecHandler) ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size, timeout time.Duration) error {
|
||||
nsenter, err := exec.LookPath("nsenter")
|
||||
if err != nil {
|
||||
return fmt.Errorf("exec unavailable - unable to locate nsenter")
|
||||
}
|
||||
|
||||
containerPid := container.State.Pid
|
||||
|
||||
// TODO what if the container doesn't have `env`???
|
||||
args := []string{"-t", fmt.Sprintf("%d", containerPid), "-m", "-i", "-u", "-n", "-p", "--", "env", "-i"}
|
||||
args = append(args, fmt.Sprintf("HOSTNAME=%s", container.Config.Hostname))
|
||||
args = append(args, container.Config.Env...)
|
||||
args = append(args, cmd...)
|
||||
command := exec.Command(nsenter, args...)
|
||||
if tty {
|
||||
p, err := kubecontainer.StartPty(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
// make sure to close the stdout stream
|
||||
defer stdout.Close()
|
||||
|
||||
kubecontainer.HandleResizing(resize, func(size term.Size) {
|
||||
term.SetSize(p.Fd(), size)
|
||||
})
|
||||
|
||||
if stdin != nil {
|
||||
go io.Copy(p, stdin)
|
||||
}
|
||||
|
||||
if stdout != nil {
|
||||
go io.Copy(stdout, p)
|
||||
}
|
||||
|
||||
err = command.Wait()
|
||||
} else {
|
||||
if stdin != nil {
|
||||
// Use an os.Pipe here as it returns true *os.File objects.
|
||||
// This way, if you run 'kubectl exec <pod> -i bash' (no tty) and type 'exit',
|
||||
// the call below to command.Run() can unblock because its Stdin is the read half
|
||||
// of the pipe.
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(w, stdin)
|
||||
|
||||
command.Stdin = r
|
||||
}
|
||||
if stdout != nil {
|
||||
command.Stdout = stdout
|
||||
}
|
||||
if stderr != nil {
|
||||
command.Stderr = stderr
|
||||
}
|
||||
|
||||
err = command.Run()
|
||||
}
|
||||
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
return &utilexec.ExitErrorWrapper{ExitError: exitErr}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NativeExecHandler executes commands in Docker containers using Docker's exec API.
|
||||
type NativeExecHandler struct{}
|
||||
|
||||
func (*NativeExecHandler) ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size, timeout time.Duration) error {
|
||||
createOpts := dockertypes.ExecConfig{
|
||||
Cmd: cmd,
|
||||
AttachStdin: stdin != nil,
|
||||
AttachStdout: stdout != nil,
|
||||
AttachStderr: stderr != nil,
|
||||
Tty: tty,
|
||||
}
|
||||
execObj, err := client.CreateExec(container.ID, createOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to exec in container - Exec setup failed - %v", err)
|
||||
}
|
||||
|
||||
// Have to start this before the call to client.StartExec because client.StartExec is a blocking
|
||||
// call :-( Otherwise, resize events don't get processed and the terminal never resizes.
|
||||
kubecontainer.HandleResizing(resize, func(size term.Size) {
|
||||
client.ResizeExecTTY(execObj.ID, int(size.Height), int(size.Width))
|
||||
})
|
||||
|
||||
startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty}
|
||||
streamOpts := StreamOptions{
|
||||
InputStream: stdin,
|
||||
OutputStream: stdout,
|
||||
ErrorStream: stderr,
|
||||
RawTerminal: tty,
|
||||
}
|
||||
err = client.StartExec(execObj.ID, startOpts, streamOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
count := 0
|
||||
for {
|
||||
inspect, err2 := client.InspectExec(execObj.ID)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if !inspect.Running {
|
||||
if inspect.ExitCode != 0 {
|
||||
err = &dockerExitError{inspect}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
count++
|
||||
if count == 5 {
|
||||
glog.Errorf("Exec session %s in container %s terminated but process still running!", execObj.ID, container.ID)
|
||||
break
|
||||
}
|
||||
|
||||
<-ticker.C
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
625
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fake_docker_client.go
generated
vendored
Normal file
625
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fake_docker_client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
78
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fake_manager.go
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fake_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/cache"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/util/oom"
|
||||
"k8s.io/kubernetes/pkg/util/procfs"
|
||||
)
|
||||
|
||||
func NewFakeDockerManager(
|
||||
client DockerInterface,
|
||||
recorder record.EventRecorder,
|
||||
livenessManager proberesults.Manager,
|
||||
containerRefManager *kubecontainer.RefManager,
|
||||
machineInfo *cadvisorapi.MachineInfo,
|
||||
podInfraContainerImage string,
|
||||
qps float32,
|
||||
burst int,
|
||||
containerLogsDir string,
|
||||
osInterface kubecontainer.OSInterface,
|
||||
networkPlugin network.NetworkPlugin,
|
||||
runtimeHelper kubecontainer.RuntimeHelper,
|
||||
httpClient kubetypes.HttpGetter, imageBackOff *flowcontrol.Backoff) *DockerManager {
|
||||
|
||||
fakeOOMAdjuster := oom.NewFakeOOMAdjuster()
|
||||
fakeProcFs := procfs.NewFakeProcFS()
|
||||
fakePodGetter := &fakePodGetter{}
|
||||
dm := NewDockerManager(client, recorder, livenessManager, containerRefManager, fakePodGetter, machineInfo, podInfraContainerImage, qps,
|
||||
burst, containerLogsDir, osInterface, networkPlugin, runtimeHelper, httpClient, &NativeExecHandler{},
|
||||
fakeOOMAdjuster, fakeProcFs, false, imageBackOff, false, false, true, "/var/lib/kubelet/seccomp")
|
||||
dm.dockerPuller = &FakeDockerPuller{}
|
||||
|
||||
// ttl of version cache is set to 0 so we always call version api directly in tests.
|
||||
dm.versionCache = cache.NewObjectCache(
|
||||
func() (interface{}, error) {
|
||||
return dm.getVersionInfo()
|
||||
},
|
||||
0,
|
||||
)
|
||||
return dm
|
||||
}
|
||||
|
||||
type fakePodGetter struct {
|
||||
pods map[types.UID]*v1.Pod
|
||||
}
|
||||
|
||||
func newFakePodGetter() *fakePodGetter {
|
||||
return &fakePodGetter{make(map[types.UID]*v1.Pod)}
|
||||
}
|
||||
|
||||
func (f *fakePodGetter) GetPodByUID(uid types.UID) (*v1.Pod, bool) {
|
||||
pod, found := f.pods[uid]
|
||||
return pod, found
|
||||
}
|
||||
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fixtures/seccomp/sub/subtest
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fixtures/seccomp/sub/subtest
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"abc": "def"
|
||||
}
|
||||
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fixtures/seccomp/test
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/fixtures/seccomp/test
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"foo": "bar"
|
||||
}
|
||||
102
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/images.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/images.go
generated
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
runtime "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
// imageStatsProvider exposes stats about all images currently available.
|
||||
type imageStatsProvider struct {
|
||||
sync.Mutex
|
||||
// layers caches the current layers, key is the layer ID.
|
||||
layers map[string]*dockertypes.ImageHistory
|
||||
// imageToLayerIDs maps image to its layer IDs.
|
||||
imageToLayerIDs map[string][]string
|
||||
// Docker remote API client
|
||||
c DockerInterface
|
||||
}
|
||||
|
||||
func newImageStatsProvider(c DockerInterface) *imageStatsProvider {
|
||||
return &imageStatsProvider{
|
||||
layers: make(map[string]*dockertypes.ImageHistory),
|
||||
imageToLayerIDs: make(map[string][]string),
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (isp *imageStatsProvider) ImageStats() (*runtime.ImageStats, error) {
|
||||
images, err := isp.c.ListImages(dockertypes.ImageListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list docker images - %v", err)
|
||||
}
|
||||
// Take the lock to protect the cache
|
||||
isp.Lock()
|
||||
defer isp.Unlock()
|
||||
// Create new cache each time, this is a little more memory consuming, but:
|
||||
// * ImageStats is only called every 10 seconds
|
||||
// * We use pointers and reference to copy cache elements.
|
||||
// The memory usage should be acceptable.
|
||||
// TODO(random-liu): Add more logic to implement in place cache update.
|
||||
newLayers := make(map[string]*dockertypes.ImageHistory)
|
||||
newImageToLayerIDs := make(map[string][]string)
|
||||
for _, image := range images {
|
||||
layerIDs, ok := isp.imageToLayerIDs[image.ID]
|
||||
if !ok {
|
||||
// Get information about the various layers of the given docker image.
|
||||
history, err := isp.c.ImageHistory(image.ID)
|
||||
if err != nil {
|
||||
// Skip the image and inspect again in next ImageStats if the image is still there
|
||||
glog.V(2).Infof("failed to get history of docker image %+v - %v", image, err)
|
||||
continue
|
||||
}
|
||||
// Cache each layer
|
||||
for i := range history {
|
||||
layer := &history[i]
|
||||
key := layer.ID
|
||||
// Some of the layers are empty.
|
||||
// We are hoping that these layers are unique to each image.
|
||||
// Still keying with the CreatedBy field to be safe.
|
||||
if key == "" || key == "<missing>" {
|
||||
key = key + layer.CreatedBy
|
||||
}
|
||||
layerIDs = append(layerIDs, key)
|
||||
newLayers[key] = layer
|
||||
}
|
||||
} else {
|
||||
for _, layerID := range layerIDs {
|
||||
newLayers[layerID] = isp.layers[layerID]
|
||||
}
|
||||
}
|
||||
newImageToLayerIDs[image.ID] = layerIDs
|
||||
}
|
||||
ret := &runtime.ImageStats{}
|
||||
// Calculate the total storage bytes
|
||||
for _, layer := range newLayers {
|
||||
ret.TotalStorageBytes += uint64(layer.Size)
|
||||
}
|
||||
// Update current cache
|
||||
isp.layers = newLayers
|
||||
isp.imageToLayerIDs = newImageToLayerIDs
|
||||
return ret, nil
|
||||
}
|
||||
334
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/images_test.go
generated
vendored
Normal file
334
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/images_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dockertools
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestImageStatsNoImages(t *testing.T) {
|
||||
fakeDockerClient := NewFakeDockerClientWithVersion("1.2.3", "1.2")
|
||||
isp := newImageStatsProvider(fakeDockerClient)
|
||||
st, err := isp.ImageStats()
|
||||
as := assert.New(t)
|
||||
as.NoError(err)
|
||||
as.NoError(fakeDockerClient.AssertCalls([]string{"list_images"}))
|
||||
as.Equal(st.TotalStorageBytes, uint64(0))
|
||||
}
|
||||
|
||||
func TestImageStatsWithImages(t *testing.T) {
|
||||
fakeDockerClient := NewFakeDockerClientWithVersion("1.2.3", "1.2")
|
||||
fakeHistoryData := map[string][]dockertypes.ImageHistory{
|
||||
"busybox": {
|
||||
{
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "0123457",
|
||||
CreatedBy: "duplicate",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
"kubelet": {
|
||||
{
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
"busybox-new": {
|
||||
{
|
||||
ID: "01234567",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "0123457",
|
||||
CreatedBy: "duplicate",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeDockerClient.InjectImageHistory(fakeHistoryData)
|
||||
fakeDockerClient.InjectImages([]dockertypes.Image{
|
||||
{
|
||||
ID: "busybox",
|
||||
},
|
||||
{
|
||||
ID: "kubelet",
|
||||
},
|
||||
{
|
||||
ID: "busybox-new",
|
||||
},
|
||||
})
|
||||
isp := newImageStatsProvider(fakeDockerClient)
|
||||
st, err := isp.ImageStats()
|
||||
as := assert.New(t)
|
||||
as.NoError(err)
|
||||
as.NoError(fakeDockerClient.AssertCalls([]string{"list_images", "image_history", "image_history", "image_history"}))
|
||||
const expectedOutput uint64 = 1300
|
||||
as.Equal(expectedOutput, st.TotalStorageBytes, "expected %d, got %d", expectedOutput, st.TotalStorageBytes)
|
||||
}
|
||||
|
||||
func TestImageStatsWithCachedImages(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
oldLayers map[string]*dockertypes.ImageHistory
|
||||
oldImageToLayerIDs map[string][]string
|
||||
images []dockertypes.Image
|
||||
history map[string][]dockertypes.ImageHistory
|
||||
expectedCalls []string
|
||||
expectedLayers map[string]*dockertypes.ImageHistory
|
||||
expectedImageToLayerIDs map[string][]string
|
||||
expectedTotalStorageSize uint64
|
||||
}{
|
||||
{
|
||||
// No cache
|
||||
oldLayers: make(map[string]*dockertypes.ImageHistory),
|
||||
oldImageToLayerIDs: make(map[string][]string),
|
||||
images: []dockertypes.Image{
|
||||
{
|
||||
ID: "busybox",
|
||||
},
|
||||
{
|
||||
ID: "kubelet",
|
||||
},
|
||||
},
|
||||
history: map[string][]dockertypes.ImageHistory{
|
||||
"busybox": {
|
||||
{
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
"kubelet": {
|
||||
{
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: []string{"list_images", "image_history", "image_history"},
|
||||
expectedLayers: map[string]*dockertypes.ImageHistory{
|
||||
"0123456": {
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
"1123456": {
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
"<missing>baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
"<missing>1baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
expectedImageToLayerIDs: map[string][]string{
|
||||
"busybox": {"0123456", "<missing>baz"},
|
||||
"kubelet": {"1123456", "<missing>1baz"},
|
||||
},
|
||||
expectedTotalStorageSize: 1000,
|
||||
},
|
||||
{
|
||||
// Use cache value
|
||||
oldLayers: map[string]*dockertypes.ImageHistory{
|
||||
"0123456": {
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
"<missing>baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
oldImageToLayerIDs: map[string][]string{
|
||||
"busybox": {"0123456", "<missing>baz"},
|
||||
},
|
||||
images: []dockertypes.Image{
|
||||
{
|
||||
ID: "busybox",
|
||||
},
|
||||
{
|
||||
ID: "kubelet",
|
||||
},
|
||||
},
|
||||
history: map[string][]dockertypes.ImageHistory{
|
||||
"busybox": {
|
||||
{
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
"kubelet": {
|
||||
{
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: []string{"list_images", "image_history"},
|
||||
expectedLayers: map[string]*dockertypes.ImageHistory{
|
||||
"0123456": {
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
"1123456": {
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
"<missing>baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
"<missing>1baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
expectedImageToLayerIDs: map[string][]string{
|
||||
"busybox": {"0123456", "<missing>baz"},
|
||||
"kubelet": {"1123456", "<missing>1baz"},
|
||||
},
|
||||
expectedTotalStorageSize: 1000,
|
||||
},
|
||||
{
|
||||
// Unused cache value
|
||||
oldLayers: map[string]*dockertypes.ImageHistory{
|
||||
"0123456": {
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
"<missing>baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
oldImageToLayerIDs: map[string][]string{
|
||||
"busybox": {"0123456", "<missing>baz"},
|
||||
},
|
||||
images: []dockertypes.Image{
|
||||
{
|
||||
ID: "kubelet",
|
||||
},
|
||||
},
|
||||
history: map[string][]dockertypes.ImageHistory{
|
||||
"kubelet": {
|
||||
{
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: []string{"list_images", "image_history"},
|
||||
expectedLayers: map[string]*dockertypes.ImageHistory{
|
||||
"1123456": {
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
"<missing>1baz": {
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
expectedImageToLayerIDs: map[string][]string{
|
||||
"kubelet": {"1123456", "<missing>1baz"},
|
||||
},
|
||||
expectedTotalStorageSize: 600,
|
||||
},
|
||||
} {
|
||||
fakeDockerClient := NewFakeDockerClientWithVersion("1.2.3", "1.2")
|
||||
fakeDockerClient.InjectImages(test.images)
|
||||
fakeDockerClient.InjectImageHistory(test.history)
|
||||
isp := newImageStatsProvider(fakeDockerClient)
|
||||
isp.layers = test.oldLayers
|
||||
isp.imageToLayerIDs = test.oldImageToLayerIDs
|
||||
st, err := isp.ImageStats()
|
||||
as := assert.New(t)
|
||||
as.NoError(err)
|
||||
as.NoError(fakeDockerClient.AssertCalls(test.expectedCalls))
|
||||
as.Equal(test.expectedLayers, isp.layers, "expected %+v, got %+v", test.expectedLayers, isp.layers)
|
||||
as.Equal(test.expectedImageToLayerIDs, isp.imageToLayerIDs, "expected %+v, got %+v", test.expectedImageToLayerIDs, isp.imageToLayerIDs)
|
||||
as.Equal(test.expectedTotalStorageSize, st.TotalStorageBytes, "expected %d, got %d", test.expectedTotalStorageSize, st.TotalStorageBytes)
|
||||
}
|
||||
}
|
||||
242
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/instrumented_docker.go
generated
vendored
Normal file
242
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/instrumented_docker.go
generated
vendored
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
)
|
||||
|
||||
// instrumentedDockerInterface wraps the DockerInterface and records the operations
|
||||
// and errors metrics.
|
||||
type instrumentedDockerInterface struct {
|
||||
client DockerInterface
|
||||
}
|
||||
|
||||
// Creates an instrumented DockerInterface from an existing DockerInterface.
|
||||
func NewInstrumentedDockerInterface(dockerClient DockerInterface) DockerInterface {
|
||||
return instrumentedDockerInterface{
|
||||
client: dockerClient,
|
||||
}
|
||||
}
|
||||
|
||||
// recordOperation records the duration of the operation.
|
||||
func recordOperation(operation string, start time.Time) {
|
||||
metrics.DockerOperations.WithLabelValues(operation).Inc()
|
||||
metrics.DockerOperationsLatency.WithLabelValues(operation).Observe(metrics.SinceInMicroseconds(start))
|
||||
}
|
||||
|
||||
// recordError records error for metric if an error occurred.
|
||||
func recordError(operation string, err error) {
|
||||
if err != nil {
|
||||
if _, ok := err.(operationTimeout); ok {
|
||||
metrics.DockerOperationsTimeout.WithLabelValues(operation).Inc()
|
||||
}
|
||||
// Docker operation timeout error is also a docker error, so we don't add else here.
|
||||
metrics.DockerOperationsErrors.WithLabelValues(operation).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ListContainers(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
|
||||
const operation = "list_containers"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.ListContainers(options)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectContainer(id string) (*dockertypes.ContainerJSON, error) {
|
||||
const operation = "inspect_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.InspectContainer(id)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) CreateContainer(opts dockertypes.ContainerCreateConfig) (*dockertypes.ContainerCreateResponse, error) {
|
||||
const operation = "create_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.CreateContainer(opts)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) StartContainer(id string) error {
|
||||
const operation = "start_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.StartContainer(id)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) StopContainer(id string, timeout int) error {
|
||||
const operation = "stop_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.StopContainer(id, timeout)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) RemoveContainer(id string, opts dockertypes.ContainerRemoveOptions) error {
|
||||
const operation = "remove_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.RemoveContainer(id, opts)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectImageByRef(image string) (*dockertypes.ImageInspect, error) {
|
||||
const operation = "inspect_image"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.InspectImageByRef(image)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectImageByID(image string) (*dockertypes.ImageInspect, error) {
|
||||
const operation = "inspect_image"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.InspectImageByID(image)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
|
||||
const operation = "list_images"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.ListImages(opts)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) 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)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) {
|
||||
const operation = "remove_image"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
imageDelete, err := in.client.RemoveImage(image, opts)
|
||||
recordError(operation, err)
|
||||
return imageDelete, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) Logs(id string, opts dockertypes.ContainerLogsOptions, sopts StreamOptions) error {
|
||||
const operation = "logs"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.Logs(id, opts, sopts)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) Version() (*dockertypes.Version, error) {
|
||||
const operation = "version"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.Version()
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) Info() (*dockertypes.Info, error) {
|
||||
const operation = "info"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.Info()
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) CreateExec(id string, opts dockertypes.ExecConfig) (*dockertypes.ContainerExecCreateResponse, error) {
|
||||
const operation = "create_exec"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.CreateExec(id, opts)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) StartExec(startExec string, opts dockertypes.ExecStartCheck, sopts StreamOptions) error {
|
||||
const operation = "start_exec"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.StartExec(startExec, opts, sopts)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) InspectExec(id string) (*dockertypes.ContainerExecInspect, error) {
|
||||
const operation = "inspect_exec"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.InspectExec(id)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) AttachToContainer(id string, opts dockertypes.ContainerAttachOptions, sopts StreamOptions) error {
|
||||
const operation = "attach"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.AttachToContainer(id, opts, sopts)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
const operation = "image_history"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.ImageHistory(id)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ResizeExecTTY(id string, height, width int) error {
|
||||
const operation = "resize_exec"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.ResizeExecTTY(id, height, width)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ResizeContainerTTY(id string, height, width int) error {
|
||||
const operation = "resize_container"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
err := in.client.ResizeContainerTTY(id, height, width)
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
635
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/kube_docker_client.go
generated
vendored
Normal file
635
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/kube_docker_client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
245
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/labels.go
generated
vendored
Normal file
245
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/labels.go
generated
vendored
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/custommetrics"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
kubetypes "k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
// This file contains all docker label related constants and functions, including:
|
||||
// * label setters and getters
|
||||
// * label filters (maybe in the future)
|
||||
|
||||
const (
|
||||
kubernetesPodDeletionGracePeriodLabel = "io.kubernetes.pod.deletionGracePeriod"
|
||||
kubernetesPodTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
|
||||
|
||||
kubernetesContainerHashLabel = "io.kubernetes.container.hash"
|
||||
kubernetesContainerRestartCountLabel = "io.kubernetes.container.restartCount"
|
||||
kubernetesContainerTerminationMessagePathLabel = "io.kubernetes.container.terminationMessagePath"
|
||||
kubernetesContainerPreStopHandlerLabel = "io.kubernetes.container.preStopHandler"
|
||||
kubernetesContainerPortsLabel = "io.kubernetes.container.ports" // Added in 1.4
|
||||
|
||||
// TODO(random-liu): Keep this for old containers, remove this when we drop support for v1.1.
|
||||
kubernetesPodLabel = "io.kubernetes.pod.data"
|
||||
|
||||
cadvisorPrometheusMetricsLabel = "io.cadvisor.metric.prometheus"
|
||||
)
|
||||
|
||||
// Container information which has been labelled on each docker container
|
||||
// TODO(random-liu): The type of Hash should be compliance with kubelet container status.
|
||||
type labelledContainerInfo struct {
|
||||
PodName string
|
||||
PodNamespace string
|
||||
PodUID kubetypes.UID
|
||||
PodDeletionGracePeriod *int64
|
||||
PodTerminationGracePeriod *int64
|
||||
Name string
|
||||
Hash string
|
||||
RestartCount int
|
||||
TerminationMessagePath string
|
||||
PreStopHandler *v1.Handler
|
||||
Ports []v1.ContainerPort
|
||||
}
|
||||
|
||||
func newLabels(container *v1.Container, pod *v1.Pod, restartCount int, enableCustomMetrics bool) map[string]string {
|
||||
labels := map[string]string{}
|
||||
labels[types.KubernetesPodNameLabel] = pod.Name
|
||||
labels[types.KubernetesPodNamespaceLabel] = pod.Namespace
|
||||
labels[types.KubernetesPodUIDLabel] = string(pod.UID)
|
||||
if pod.DeletionGracePeriodSeconds != nil {
|
||||
labels[kubernetesPodDeletionGracePeriodLabel] = strconv.FormatInt(*pod.DeletionGracePeriodSeconds, 10)
|
||||
}
|
||||
if pod.Spec.TerminationGracePeriodSeconds != nil {
|
||||
labels[kubernetesPodTerminationGracePeriodLabel] = strconv.FormatInt(*pod.Spec.TerminationGracePeriodSeconds, 10)
|
||||
}
|
||||
|
||||
labels[types.KubernetesContainerNameLabel] = container.Name
|
||||
labels[kubernetesContainerHashLabel] = strconv.FormatUint(kubecontainer.HashContainer(container), 16)
|
||||
labels[kubernetesContainerRestartCountLabel] = strconv.Itoa(restartCount)
|
||||
labels[kubernetesContainerTerminationMessagePathLabel] = container.TerminationMessagePath
|
||||
if container.Lifecycle != nil && container.Lifecycle.PreStop != nil {
|
||||
// Using json enconding so that the PreStop handler object is readable after writing as a label
|
||||
rawPreStop, err := json.Marshal(container.Lifecycle.PreStop)
|
||||
if err != nil {
|
||||
glog.Errorf("Unable to marshal lifecycle PreStop handler for container %q of pod %q: %v", container.Name, format.Pod(pod), err)
|
||||
} else {
|
||||
labels[kubernetesContainerPreStopHandlerLabel] = string(rawPreStop)
|
||||
}
|
||||
}
|
||||
if len(container.Ports) > 0 {
|
||||
rawContainerPorts, err := json.Marshal(container.Ports)
|
||||
if err != nil {
|
||||
glog.Errorf("Unable to marshal container ports for container %q for pod %q: %v", container.Name, format.Pod(pod), err)
|
||||
} else {
|
||||
labels[kubernetesContainerPortsLabel] = string(rawContainerPorts)
|
||||
}
|
||||
}
|
||||
if enableCustomMetrics {
|
||||
path, err := custommetrics.GetCAdvisorCustomMetricsDefinitionPath(container)
|
||||
if path != nil && err == nil {
|
||||
labels[cadvisorPrometheusMetricsLabel] = *path
|
||||
}
|
||||
}
|
||||
|
||||
return labels
|
||||
}
|
||||
|
||||
func getContainerInfoFromLabel(labels map[string]string) *labelledContainerInfo {
|
||||
var err error
|
||||
containerInfo := &labelledContainerInfo{
|
||||
PodName: getStringValueFromLabel(labels, types.KubernetesPodNameLabel),
|
||||
PodNamespace: getStringValueFromLabel(labels, types.KubernetesPodNamespaceLabel),
|
||||
PodUID: kubetypes.UID(getStringValueFromLabel(labels, types.KubernetesPodUIDLabel)),
|
||||
Name: getStringValueFromLabel(labels, types.KubernetesContainerNameLabel),
|
||||
Hash: getStringValueFromLabel(labels, kubernetesContainerHashLabel),
|
||||
TerminationMessagePath: getStringValueFromLabel(labels, kubernetesContainerTerminationMessagePathLabel),
|
||||
}
|
||||
if containerInfo.RestartCount, err = getIntValueFromLabel(labels, kubernetesContainerRestartCountLabel); err != nil {
|
||||
logError(containerInfo, kubernetesContainerRestartCountLabel, err)
|
||||
}
|
||||
if containerInfo.PodDeletionGracePeriod, err = getInt64PointerFromLabel(labels, kubernetesPodDeletionGracePeriodLabel); err != nil {
|
||||
logError(containerInfo, kubernetesPodDeletionGracePeriodLabel, err)
|
||||
}
|
||||
if containerInfo.PodTerminationGracePeriod, err = getInt64PointerFromLabel(labels, kubernetesPodTerminationGracePeriodLabel); err != nil {
|
||||
logError(containerInfo, kubernetesPodTerminationGracePeriodLabel, err)
|
||||
}
|
||||
preStopHandler := &v1.Handler{}
|
||||
if found, err := getJsonObjectFromLabel(labels, kubernetesContainerPreStopHandlerLabel, preStopHandler); err != nil {
|
||||
logError(containerInfo, kubernetesContainerPreStopHandlerLabel, err)
|
||||
} else if found {
|
||||
containerInfo.PreStopHandler = preStopHandler
|
||||
}
|
||||
containerPorts := []v1.ContainerPort{}
|
||||
if found, err := getJsonObjectFromLabel(labels, kubernetesContainerPortsLabel, &containerPorts); err != nil {
|
||||
logError(containerInfo, kubernetesContainerPortsLabel, err)
|
||||
} else if found {
|
||||
containerInfo.Ports = containerPorts
|
||||
}
|
||||
supplyContainerInfoWithOldLabel(labels, containerInfo)
|
||||
return containerInfo
|
||||
}
|
||||
|
||||
func getStringValueFromLabel(labels map[string]string, label string) string {
|
||||
if value, found := labels[label]; found {
|
||||
return value
|
||||
}
|
||||
// Do not report error, because there should be many old containers without label now.
|
||||
glog.V(3).Infof("Container doesn't have label %s, it may be an old or invalid container", label)
|
||||
// Return empty string "" for these containers, the caller will get value by other ways.
|
||||
return ""
|
||||
}
|
||||
|
||||
func getIntValueFromLabel(labels map[string]string, label string) (int, error) {
|
||||
if strValue, found := labels[label]; found {
|
||||
intValue, err := strconv.Atoi(strValue)
|
||||
if err != nil {
|
||||
// This really should not happen. Just set value to 0 to handle this abnormal case
|
||||
return 0, err
|
||||
}
|
||||
return intValue, nil
|
||||
}
|
||||
// Do not report error, because there should be many old containers without label now.
|
||||
glog.V(3).Infof("Container doesn't have label %s, it may be an old or invalid container", label)
|
||||
// Just set the value to 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func getInt64PointerFromLabel(labels map[string]string, label string) (*int64, error) {
|
||||
if strValue, found := labels[label]; found {
|
||||
int64Value, err := strconv.ParseInt(strValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &int64Value, nil
|
||||
}
|
||||
// Because it's normal that a container has no PodDeletionGracePeriod and PodTerminationGracePeriod label,
|
||||
// don't report any error here.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getJsonObjectFromLabel returns a bool value indicating whether an object is found
|
||||
func getJsonObjectFromLabel(labels map[string]string, label string, value interface{}) (bool, error) {
|
||||
if strValue, found := labels[label]; found {
|
||||
err := json.Unmarshal([]byte(strValue), value)
|
||||
return found, err
|
||||
}
|
||||
// Because it's normal that a container has no PreStopHandler label, don't report any error here.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// The label kubernetesPodLabel is added a long time ago (#7421), it serialized the whole v1.Pod to a docker label.
|
||||
// We want to remove this label because it serialized too much useless information. However kubelet may still work
|
||||
// with old containers which only have this label for a long time until we completely deprecate the old label.
|
||||
// Before that to ensure correctness we have to supply information with the old labels when newly added labels
|
||||
// are not available.
|
||||
// TODO(random-liu): Remove this function when we can completely remove label kubernetesPodLabel, probably after
|
||||
// dropping support for v1.1.
|
||||
func supplyContainerInfoWithOldLabel(labels map[string]string, containerInfo *labelledContainerInfo) {
|
||||
// Get v1.Pod from old label
|
||||
var pod *v1.Pod
|
||||
data, found := labels[kubernetesPodLabel]
|
||||
if !found {
|
||||
// Don't report any error here, because it's normal that a container has no pod label, especially
|
||||
// when we gradually deprecate the old label
|
||||
return
|
||||
}
|
||||
pod = &v1.Pod{}
|
||||
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(data), pod); err != nil {
|
||||
// If the pod label can't be parsed, we should report an error
|
||||
logError(containerInfo, kubernetesPodLabel, err)
|
||||
return
|
||||
}
|
||||
if containerInfo.PodDeletionGracePeriod == nil {
|
||||
containerInfo.PodDeletionGracePeriod = pod.DeletionGracePeriodSeconds
|
||||
}
|
||||
if containerInfo.PodTerminationGracePeriod == nil {
|
||||
containerInfo.PodTerminationGracePeriod = pod.Spec.TerminationGracePeriodSeconds
|
||||
}
|
||||
|
||||
// Get v1.Container from v1.Pod
|
||||
var container *v1.Container
|
||||
for i := range pod.Spec.Containers {
|
||||
if pod.Spec.Containers[i].Name == containerInfo.Name {
|
||||
container = &pod.Spec.Containers[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if container == nil {
|
||||
glog.Errorf("Unable to find container %q in pod %q", containerInfo.Name, format.Pod(pod))
|
||||
return
|
||||
}
|
||||
if containerInfo.PreStopHandler == nil && container.Lifecycle != nil {
|
||||
containerInfo.PreStopHandler = container.Lifecycle.PreStop
|
||||
}
|
||||
}
|
||||
|
||||
func logError(containerInfo *labelledContainerInfo, label string, err error) {
|
||||
glog.Errorf("Unable to get %q for container %q of pod %q: %v", label, containerInfo.Name,
|
||||
kubecontainer.BuildPodFullName(containerInfo.PodName, containerInfo.PodNamespace), err)
|
||||
}
|
||||
141
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/labels_test.go
generated
vendored
Normal file
141
vendor/k8s.io/kubernetes/pkg/kubelet/dockertools/labels_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright 2015 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 dockertools
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func TestLabels(t *testing.T) {
|
||||
restartCount := 5
|
||||
deletionGracePeriod := int64(10)
|
||||
terminationGracePeriod := int64(10)
|
||||
lifecycle := &v1.Lifecycle{
|
||||
// Left PostStart as nil
|
||||
PreStop: &v1.Handler{
|
||||
Exec: &v1.ExecAction{
|
||||
Command: []string{"action1", "action2"},
|
||||
},
|
||||
HTTPGet: &v1.HTTPGetAction{
|
||||
Path: "path",
|
||||
Host: "host",
|
||||
Port: intstr.FromInt(8080),
|
||||
Scheme: "scheme",
|
||||
},
|
||||
TCPSocket: &v1.TCPSocketAction{
|
||||
Port: intstr.FromString("80"),
|
||||
},
|
||||
},
|
||||
}
|
||||
containerPorts := []v1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
HostPort: 80,
|
||||
ContainerPort: 8080,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "https",
|
||||
HostPort: 443,
|
||||
ContainerPort: 6443,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
}
|
||||
container := &v1.Container{
|
||||
Name: "test_container",
|
||||
Ports: containerPorts,
|
||||
TerminationMessagePath: "/somepath",
|
||||
Lifecycle: lifecycle,
|
||||
}
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "test_pod",
|
||||
Namespace: "test_pod_namespace",
|
||||
UID: "test_pod_uid",
|
||||
DeletionGracePeriodSeconds: &deletionGracePeriod,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{*container},
|
||||
TerminationGracePeriodSeconds: &terminationGracePeriod,
|
||||
},
|
||||
}
|
||||
expected := &labelledContainerInfo{
|
||||
PodName: pod.Name,
|
||||
PodNamespace: pod.Namespace,
|
||||
PodUID: pod.UID,
|
||||
PodDeletionGracePeriod: pod.DeletionGracePeriodSeconds,
|
||||
PodTerminationGracePeriod: pod.Spec.TerminationGracePeriodSeconds,
|
||||
Name: container.Name,
|
||||
Hash: strconv.FormatUint(kubecontainer.HashContainer(container), 16),
|
||||
RestartCount: restartCount,
|
||||
TerminationMessagePath: container.TerminationMessagePath,
|
||||
PreStopHandler: container.Lifecycle.PreStop,
|
||||
Ports: containerPorts,
|
||||
}
|
||||
|
||||
// Test whether we can get right information from label
|
||||
labels := newLabels(container, pod, restartCount, false)
|
||||
containerInfo := getContainerInfoFromLabel(labels)
|
||||
if !reflect.DeepEqual(containerInfo, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, containerInfo)
|
||||
}
|
||||
|
||||
// Test when DeletionGracePeriodSeconds, TerminationGracePeriodSeconds and Lifecycle are nil,
|
||||
// the information got from label should also be nil
|
||||
container.Lifecycle = nil
|
||||
pod.DeletionGracePeriodSeconds = nil
|
||||
pod.Spec.TerminationGracePeriodSeconds = nil
|
||||
expected.PodDeletionGracePeriod = nil
|
||||
expected.PodTerminationGracePeriod = nil
|
||||
expected.PreStopHandler = nil
|
||||
// Because container is changed, the Hash should be updated
|
||||
expected.Hash = strconv.FormatUint(kubecontainer.HashContainer(container), 16)
|
||||
labels = newLabels(container, pod, restartCount, false)
|
||||
containerInfo = getContainerInfoFromLabel(labels)
|
||||
if !reflect.DeepEqual(containerInfo, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, containerInfo)
|
||||
}
|
||||
|
||||
// Test when DeletionGracePeriodSeconds, TerminationGracePeriodSeconds and Lifecycle are nil,
|
||||
// but the old label kubernetesPodLabel is set, the information got from label should also be set
|
||||
pod.DeletionGracePeriodSeconds = &deletionGracePeriod
|
||||
pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriod
|
||||
container.Lifecycle = lifecycle
|
||||
data, err := runtime.Encode(testapi.Default.Codec(), pod)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode pod %q into string: %v", format.Pod(pod), err)
|
||||
}
|
||||
labels[kubernetesPodLabel] = string(data)
|
||||
expected.PodDeletionGracePeriod = pod.DeletionGracePeriodSeconds
|
||||
expected.PodTerminationGracePeriod = pod.Spec.TerminationGracePeriodSeconds
|
||||
expected.PreStopHandler = container.Lifecycle.PreStop
|
||||
// Do not update expected.Hash here, because we directly use the labels in last test, so we never
|
||||
// changed the kubernetesContainerHashLabel in this test, the expected.Hash shouldn't be changed.
|
||||
containerInfo = getContainerInfoFromLabel(labels)
|
||||
if !reflect.DeepEqual(containerInfo, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, containerInfo)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue