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
208
vendor/k8s.io/kubernetes/pkg/kubelet/BUILD
generated
vendored
Normal file
208
vendor/k8s.io/kubernetes/pkg/kubelet/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
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 = [
|
||||
"active_deadline.go",
|
||||
"disk_manager.go",
|
||||
"doc.go",
|
||||
"kubelet.go",
|
||||
"kubelet_cadvisor.go",
|
||||
"kubelet_getters.go",
|
||||
"kubelet_network.go",
|
||||
"kubelet_node_status.go",
|
||||
"kubelet_pods.go",
|
||||
"kubelet_resources.go",
|
||||
"kubelet_volumes.go",
|
||||
"networks.go",
|
||||
"oom_watcher.go",
|
||||
"pod_container_deletor.go",
|
||||
"pod_workers.go",
|
||||
"reason_cache.go",
|
||||
"runonce.go",
|
||||
"runtime.go",
|
||||
"util.go",
|
||||
"volume_host.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubelet/app/options:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/api/v1/validation:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/apis/componentconfig/v1alpha1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/capabilities:go_default_library",
|
||||
"//pkg/client/cache:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/fieldpath:go_default_library",
|
||||
"//pkg/fields:go_default_library",
|
||||
"//pkg/kubelet/api:go_default_library",
|
||||
"//pkg/kubelet/cadvisor:go_default_library",
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/config:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim:go_default_library",
|
||||
"//pkg/kubelet/dockershim/remote:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/envvars:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/eviction:go_default_library",
|
||||
"//pkg/kubelet/images:go_default_library",
|
||||
"//pkg/kubelet/kuberuntime:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
"//pkg/kubelet/metrics:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/pleg:go_default_library",
|
||||
"//pkg/kubelet/pod:go_default_library",
|
||||
"//pkg/kubelet/prober:go_default_library",
|
||||
"//pkg/kubelet/prober/results:go_default_library",
|
||||
"//pkg/kubelet/remote:go_default_library",
|
||||
"//pkg/kubelet/rkt:go_default_library",
|
||||
"//pkg/kubelet/server:go_default_library",
|
||||
"//pkg/kubelet/server/remotecommand:go_default_library",
|
||||
"//pkg/kubelet/server/stats:go_default_library",
|
||||
"//pkg/kubelet/server/streaming:go_default_library",
|
||||
"//pkg/kubelet/status:go_default_library",
|
||||
"//pkg/kubelet/sysctl:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/format:go_default_library",
|
||||
"//pkg/kubelet/util/queue:go_default_library",
|
||||
"//pkg/kubelet/util/sliceutils:go_default_library",
|
||||
"//pkg/kubelet/volumemanager:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"//pkg/util/bandwidth:go_default_library",
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/config:go_default_library",
|
||||
"//pkg/util/dbus:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/integer:go_default_library",
|
||||
"//pkg/util/io:go_default_library",
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/net:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//pkg/util/oom:go_default_library",
|
||||
"//pkg/util/procfs:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//pkg/util/validation:go_default_library",
|
||||
"//pkg/util/validation/field:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
|
||||
"//third_party/forked/golang/expansion:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/golang/groupcache/lru",
|
||||
"//vendor:github.com/google/cadvisor/events",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/google/cadvisor/info/v2",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"active_deadline_test.go",
|
||||
"disk_manager_test.go",
|
||||
"kubelet_cadvisor_test.go",
|
||||
"kubelet_getters_test.go",
|
||||
"kubelet_network_test.go",
|
||||
"kubelet_node_status_test.go",
|
||||
"kubelet_pods_test.go",
|
||||
"kubelet_resources_test.go",
|
||||
"kubelet_test.go",
|
||||
"kubelet_volumes_test.go",
|
||||
"oom_watcher_test.go",
|
||||
"pod_container_deletor_test.go",
|
||||
"pod_workers_test.go",
|
||||
"reason_cache_test.go",
|
||||
"runonce_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/capabilities:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5/fake:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/client/testing/core:go_default_library",
|
||||
"//pkg/kubelet/cadvisor/testing:go_default_library",
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/config:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/eviction:go_default_library",
|
||||
"//pkg/kubelet/images:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/testing:go_default_library",
|
||||
"//pkg/kubelet/pleg:go_default_library",
|
||||
"//pkg/kubelet/pod:go_default_library",
|
||||
"//pkg/kubelet/pod/testing:go_default_library",
|
||||
"//pkg/kubelet/prober/results:go_default_library",
|
||||
"//pkg/kubelet/prober/testing:go_default_library",
|
||||
"//pkg/kubelet/server/remotecommand:go_default_library",
|
||||
"//pkg/kubelet/server/stats:go_default_library",
|
||||
"//pkg/kubelet/status:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/queue:go_default_library",
|
||||
"//pkg/kubelet/util/sliceutils:go_default_library",
|
||||
"//pkg/kubelet/volumemanager:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/bandwidth:go_default_library",
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/diff:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/rand:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/testing:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/host_path:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/google/cadvisor/info/v2",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
"//vendor:github.com/stretchr/testify/require",
|
||||
],
|
||||
)
|
||||
4
vendor/k8s.io/kubernetes/pkg/kubelet/OWNERS
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/pkg/kubelet/OWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
assignees:
|
||||
- dchen1107
|
||||
- vishh
|
||||
- yujuhong
|
||||
98
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline.go
generated
vendored
Normal file
98
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
"k8s.io/kubernetes/pkg/kubelet/status"
|
||||
"k8s.io/kubernetes/pkg/util/clock"
|
||||
)
|
||||
|
||||
const (
|
||||
reason = "DeadlineExceeded"
|
||||
message = "Pod was active on the node longer than the specified deadline"
|
||||
)
|
||||
|
||||
// activeDeadlineHandler knows how to enforce active deadlines on pods.
|
||||
type activeDeadlineHandler struct {
|
||||
// the clock to use for deadline enforcement
|
||||
clock clock.Clock
|
||||
// the provider of pod status
|
||||
podStatusProvider status.PodStatusProvider
|
||||
// the recorder to dispatch events when we identify a pod has exceeded active deadline
|
||||
recorder record.EventRecorder
|
||||
}
|
||||
|
||||
// newActiveDeadlineHandler returns an active deadline handler that can enforce pod active deadline
|
||||
func newActiveDeadlineHandler(
|
||||
podStatusProvider status.PodStatusProvider,
|
||||
recorder record.EventRecorder,
|
||||
clock clock.Clock,
|
||||
) (*activeDeadlineHandler, error) {
|
||||
|
||||
// check for all required fields
|
||||
if clock == nil || podStatusProvider == nil || recorder == nil {
|
||||
return nil, fmt.Errorf("Required arguments must not be nil: %v, %v, %v", clock, podStatusProvider, recorder)
|
||||
}
|
||||
return &activeDeadlineHandler{
|
||||
clock: clock,
|
||||
podStatusProvider: podStatusProvider,
|
||||
recorder: recorder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ShouldSync returns true if the pod is past its active deadline.
|
||||
func (m *activeDeadlineHandler) ShouldSync(pod *v1.Pod) bool {
|
||||
return m.pastActiveDeadline(pod)
|
||||
}
|
||||
|
||||
// ShouldEvict returns true if the pod is past its active deadline.
|
||||
// It dispatches an event that the pod should be evicted if it is past its deadline.
|
||||
func (m *activeDeadlineHandler) ShouldEvict(pod *v1.Pod) lifecycle.ShouldEvictResponse {
|
||||
if !m.pastActiveDeadline(pod) {
|
||||
return lifecycle.ShouldEvictResponse{Evict: false}
|
||||
}
|
||||
m.recorder.Eventf(pod, v1.EventTypeNormal, reason, message)
|
||||
return lifecycle.ShouldEvictResponse{Evict: true, Reason: reason, Message: message}
|
||||
}
|
||||
|
||||
// pastActiveDeadline returns true if the pod has been active for more than its ActiveDeadlineSeconds
|
||||
func (m *activeDeadlineHandler) pastActiveDeadline(pod *v1.Pod) bool {
|
||||
// no active deadline was specified
|
||||
if pod.Spec.ActiveDeadlineSeconds == nil {
|
||||
return false
|
||||
}
|
||||
// get the latest status to determine if it was started
|
||||
podStatus, ok := m.podStatusProvider.GetPodStatus(pod.UID)
|
||||
if !ok {
|
||||
podStatus = pod.Status
|
||||
}
|
||||
// we have no start time so just return
|
||||
if podStatus.StartTime.IsZero() {
|
||||
return false
|
||||
}
|
||||
// determine if the deadline was exceeded
|
||||
start := podStatus.StartTime.Time
|
||||
duration := m.clock.Since(start)
|
||||
allowedDuration := time.Duration(*pod.Spec.ActiveDeadlineSeconds) * time.Second
|
||||
return duration >= allowedDuration
|
||||
}
|
||||
95
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline_test.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/clock"
|
||||
)
|
||||
|
||||
// mockPodStatusProvider returns the status on the specified pod
|
||||
type mockPodStatusProvider struct {
|
||||
pods []*v1.Pod
|
||||
}
|
||||
|
||||
// GetPodStatus returns the status on the associated pod with matching uid (if found)
|
||||
func (m *mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
|
||||
for _, pod := range m.pods {
|
||||
if pod.UID == uid {
|
||||
return pod.Status, true
|
||||
}
|
||||
}
|
||||
return v1.PodStatus{}, false
|
||||
}
|
||||
|
||||
// TestActiveDeadlineHandler verifies the active deadline handler functions as expected.
|
||||
func TestActiveDeadlineHandler(t *testing.T) {
|
||||
pods := newTestPods(4)
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
podStatusProvider := &mockPodStatusProvider{pods: pods}
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
handler, err := newActiveDeadlineHandler(podStatusProvider, fakeRecorder, fakeClock)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
now := metav1.Now()
|
||||
startTime := metav1.NewTime(now.Time.Add(-1 * time.Minute))
|
||||
|
||||
// this pod has exceeded its active deadline
|
||||
exceededActiveDeadlineSeconds := int64(30)
|
||||
pods[0].Status.StartTime = &startTime
|
||||
pods[0].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds
|
||||
|
||||
// this pod has not exceeded its active deadline
|
||||
notYetActiveDeadlineSeconds := int64(120)
|
||||
pods[1].Status.StartTime = &startTime
|
||||
pods[1].Spec.ActiveDeadlineSeconds = ¬YetActiveDeadlineSeconds
|
||||
|
||||
// this pod has no deadline
|
||||
pods[2].Status.StartTime = &startTime
|
||||
pods[2].Spec.ActiveDeadlineSeconds = nil
|
||||
|
||||
testCases := []struct {
|
||||
pod *v1.Pod
|
||||
expected bool
|
||||
}{{pods[0], true}, {pods[1], false}, {pods[2], false}, {pods[3], false}}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
if actual := handler.ShouldSync(testCase.pod); actual != testCase.expected {
|
||||
t.Errorf("[%d] ShouldSync expected %#v, got %#v", i, testCase.expected, actual)
|
||||
}
|
||||
actual := handler.ShouldEvict(testCase.pod)
|
||||
if actual.Evict != testCase.expected {
|
||||
t.Errorf("[%d] ShouldEvict.Evict expected %#v, got %#v", i, testCase.expected, actual.Evict)
|
||||
}
|
||||
if testCase.expected {
|
||||
if actual.Reason != reason {
|
||||
t.Errorf("[%d] ShouldEvict.Reason expected %#v, got %#v", i, message, actual.Reason)
|
||||
}
|
||||
if actual.Message != message {
|
||||
t.Errorf("[%d] ShouldEvict.Message expected %#v, got %#v", i, message, actual.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
vendor/k8s.io/kubernetes/pkg/kubelet/api/BUILD
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/kubelet/api/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
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 = ["services.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/kubelet/api/v1alpha1/runtime:go_default_library"],
|
||||
)
|
||||
100
vendor/k8s.io/kubernetes/pkg/kubelet/api/services.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/pkg/kubelet/api/services.go
generated
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
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 api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
// RuntimeVersioner contains methods for runtime name, version and API version.
|
||||
type RuntimeVersioner interface {
|
||||
// Version returns the runtime name, runtime version and runtime API version
|
||||
Version(apiVersion string) (*runtimeapi.VersionResponse, error)
|
||||
}
|
||||
|
||||
// ContainerManager contains methods to manipulate containers managed by a
|
||||
// container runtime. The methods are thread-safe.
|
||||
type ContainerManager interface {
|
||||
// CreateContainer creates a new container in specified PodSandbox.
|
||||
CreateContainer(podSandboxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
|
||||
// StartContainer starts the container.
|
||||
StartContainer(containerID string) error
|
||||
// StopContainer stops a running container with a grace period (i.e., timeout).
|
||||
StopContainer(containerID string, timeout int64) error
|
||||
// RemoveContainer removes the container.
|
||||
RemoveContainer(containerID string) error
|
||||
// ListContainers lists all containers by filters.
|
||||
ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error)
|
||||
// ContainerStatus returns the status of the container.
|
||||
ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error)
|
||||
// ExecSync executes a command in the container, and returns the stdout output.
|
||||
// If command exits with a non-zero exit code, an error is returned.
|
||||
ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error)
|
||||
// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
|
||||
Exec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
|
||||
// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
|
||||
Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
|
||||
}
|
||||
|
||||
// PodSandboxManager contains methods for operating on PodSandboxes. The methods
|
||||
// are thread-safe.
|
||||
type PodSandboxManager interface {
|
||||
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
|
||||
// the sandbox is in ready state.
|
||||
RunPodSandbox(config *runtimeapi.PodSandboxConfig) (string, error)
|
||||
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
||||
// sandbox, they should be force terminated.
|
||||
StopPodSandbox(podSandboxID string) error
|
||||
// RemovePodSandbox removes the sandbox. If there are running containers in the
|
||||
// sandbox, they should be forcibly removed.
|
||||
RemovePodSandbox(podSandboxID string) error
|
||||
// PodSandboxStatus returns the Status of the PodSandbox.
|
||||
PodSandboxStatus(podSandboxID string) (*runtimeapi.PodSandboxStatus, error)
|
||||
// ListPodSandbox returns a list of Sandbox.
|
||||
ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error)
|
||||
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
|
||||
PortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)
|
||||
}
|
||||
|
||||
// RuntimeService interface should be implemented by a container runtime.
|
||||
// The methods should be thread-safe.
|
||||
type RuntimeService interface {
|
||||
RuntimeVersioner
|
||||
ContainerManager
|
||||
PodSandboxManager
|
||||
|
||||
// UpdateRuntimeConfig updates runtime configuration if specified
|
||||
UpdateRuntimeConfig(runtimeConfig *runtimeapi.RuntimeConfig) error
|
||||
// Status returns the status of the runtime.
|
||||
Status() (*runtimeapi.RuntimeStatus, error)
|
||||
}
|
||||
|
||||
// ImageManagerService interface should be implemented by a container image
|
||||
// manager.
|
||||
// The methods should be thread-safe.
|
||||
type ImageManagerService interface {
|
||||
// ListImages lists the existing images.
|
||||
ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error)
|
||||
// ImageStatus returns the status of the image.
|
||||
ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error)
|
||||
// PullImage pulls an image with the authentication config.
|
||||
PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig) error
|
||||
// RemoveImage removes the image.
|
||||
RemoveImage(image *runtimeapi.ImageSpec) error
|
||||
}
|
||||
25
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/BUILD
generated
vendored
Normal file
25
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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 = [
|
||||
"fake_image_service.go",
|
||||
"fake_runtime_service.go",
|
||||
"utils.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
|
||||
"//pkg/kubelet/util/sliceutils:go_default_library",
|
||||
],
|
||||
)
|
||||
120
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/fake_image_service.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/fake_image_service.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
|
||||
)
|
||||
|
||||
type FakeImageService struct {
|
||||
sync.Mutex
|
||||
|
||||
FakeImageSize uint64
|
||||
Called []string
|
||||
Images map[string]*runtimeapi.Image
|
||||
}
|
||||
|
||||
func (r *FakeImageService) SetFakeImages(images []string) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Images = make(map[string]*runtimeapi.Image)
|
||||
for _, image := range images {
|
||||
r.Images[image] = r.makeFakeImage(image)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeImageService) SetFakeImageSize(size uint64) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.FakeImageSize = size
|
||||
}
|
||||
|
||||
func NewFakeImageService() *FakeImageService {
|
||||
return &FakeImageService{
|
||||
Called: make([]string, 0),
|
||||
Images: make(map[string]*runtimeapi.Image),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeImageService) makeFakeImage(image string) *runtimeapi.Image {
|
||||
return &runtimeapi.Image{
|
||||
Id: &image,
|
||||
Size_: &r.FakeImageSize,
|
||||
RepoTags: []string{image},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeImageService) ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ListImages")
|
||||
|
||||
images := make([]*runtimeapi.Image, 0)
|
||||
for _, img := range r.Images {
|
||||
if filter != nil && filter.Image != nil {
|
||||
if !sliceutils.StringInSlice(filter.Image.GetImage(), img.RepoTags) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
images = append(images, img)
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (r *FakeImageService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ImageStatus")
|
||||
|
||||
return r.Images[image.GetImage()], nil
|
||||
}
|
||||
|
||||
func (r *FakeImageService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "PullImage")
|
||||
|
||||
// ImageID should be randomized for real container runtime, but here just use
|
||||
// image's name for easily making fake images.
|
||||
imageID := image.GetImage()
|
||||
if _, ok := r.Images[imageID]; !ok {
|
||||
r.Images[imageID] = r.makeFakeImage(image.GetImage())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeImageService) RemoveImage(image *runtimeapi.ImageSpec) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "RemoveImage")
|
||||
|
||||
// Remove the image
|
||||
delete(r.Images, image.GetImage())
|
||||
|
||||
return nil
|
||||
}
|
||||
395
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/fake_runtime_service.go
generated
vendored
Normal file
395
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/fake_runtime_service.go
generated
vendored
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.1.0"
|
||||
|
||||
FakeRuntimeName = "fakeRuntime"
|
||||
FakePodSandboxIP = "192.168.192.168"
|
||||
)
|
||||
|
||||
type FakePodSandbox struct {
|
||||
// PodSandboxStatus contains the runtime information for a sandbox.
|
||||
runtimeapi.PodSandboxStatus
|
||||
}
|
||||
|
||||
type FakeContainer struct {
|
||||
// ContainerStatus contains the runtime information for a container.
|
||||
runtimeapi.ContainerStatus
|
||||
|
||||
// the sandbox id of this container
|
||||
SandboxID string
|
||||
}
|
||||
|
||||
type FakeRuntimeService struct {
|
||||
sync.Mutex
|
||||
|
||||
Called []string
|
||||
|
||||
FakeStatus *runtimeapi.RuntimeStatus
|
||||
Containers map[string]*FakeContainer
|
||||
Sandboxes map[string]*FakePodSandbox
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) SetFakeSandboxes(sandboxes []*FakePodSandbox) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Sandboxes = make(map[string]*FakePodSandbox)
|
||||
for _, sandbox := range sandboxes {
|
||||
sandboxID := sandbox.GetId()
|
||||
r.Sandboxes[sandboxID] = sandbox
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) SetFakeContainers(containers []*FakeContainer) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Containers = make(map[string]*FakeContainer)
|
||||
for _, c := range containers {
|
||||
containerID := c.GetId()
|
||||
r.Containers[containerID] = c
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) AssertCalls(calls []string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
if !reflect.DeepEqual(calls, r.Called) {
|
||||
return fmt.Errorf("expected %#v, got %#v", calls, r.Called)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFakeRuntimeService() *FakeRuntimeService {
|
||||
return &FakeRuntimeService{
|
||||
Called: make([]string, 0),
|
||||
Containers: make(map[string]*FakeContainer),
|
||||
Sandboxes: make(map[string]*FakePodSandbox),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) Version(apiVersion string) (*runtimeapi.VersionResponse, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "Version")
|
||||
|
||||
return &runtimeapi.VersionResponse{
|
||||
Version: &version,
|
||||
RuntimeName: &FakeRuntimeName,
|
||||
RuntimeVersion: &version,
|
||||
RuntimeApiVersion: &version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) Status() (*runtimeapi.RuntimeStatus, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "Status")
|
||||
|
||||
return r.FakeStatus, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) RunPodSandbox(config *runtimeapi.PodSandboxConfig) (string, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "RunPodSandbox")
|
||||
|
||||
// PodSandboxID should be randomized for real container runtime, but here just use
|
||||
// fixed name from BuildSandboxName() for easily making fake sandboxes.
|
||||
podSandboxID := BuildSandboxName(config.Metadata)
|
||||
createdAt := time.Now().Unix()
|
||||
readyState := runtimeapi.PodSandboxState_SANDBOX_READY
|
||||
r.Sandboxes[podSandboxID] = &FakePodSandbox{
|
||||
PodSandboxStatus: runtimeapi.PodSandboxStatus{
|
||||
Id: &podSandboxID,
|
||||
Metadata: config.Metadata,
|
||||
State: &readyState,
|
||||
CreatedAt: &createdAt,
|
||||
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||
Ip: &FakePodSandboxIP,
|
||||
},
|
||||
Labels: config.Labels,
|
||||
Annotations: config.Annotations,
|
||||
},
|
||||
}
|
||||
|
||||
return podSandboxID, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) StopPodSandbox(podSandboxID string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "StopPodSandbox")
|
||||
|
||||
notReadyState := runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
||||
if s, ok := r.Sandboxes[podSandboxID]; ok {
|
||||
s.State = ¬ReadyState
|
||||
} else {
|
||||
return fmt.Errorf("pod sandbox %s not found", podSandboxID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) RemovePodSandbox(podSandboxID string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "RemovePodSandbox")
|
||||
|
||||
// Remove the pod sandbox
|
||||
delete(r.Sandboxes, podSandboxID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeapi.PodSandboxStatus, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "PodSandboxStatus")
|
||||
|
||||
s, ok := r.Sandboxes[podSandboxID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("pod sandbox %q not found", podSandboxID)
|
||||
}
|
||||
|
||||
status := s.PodSandboxStatus
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ListPodSandbox")
|
||||
|
||||
result := make([]*runtimeapi.PodSandbox, 0)
|
||||
for id, s := range r.Sandboxes {
|
||||
if filter != nil {
|
||||
if filter.Id != nil && filter.GetId() != id {
|
||||
continue
|
||||
}
|
||||
if filter.State != nil && filter.GetState() != s.GetState() {
|
||||
continue
|
||||
}
|
||||
if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &runtimeapi.PodSandbox{
|
||||
Id: s.Id,
|
||||
Metadata: s.Metadata,
|
||||
State: s.State,
|
||||
CreatedAt: s.CreatedAt,
|
||||
Labels: s.Labels,
|
||||
Annotations: s.Annotations,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) PortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "PortForward")
|
||||
return &runtimeapi.PortForwardResponse{}, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) CreateContainer(podSandboxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "CreateContainer")
|
||||
|
||||
// ContainerID should be randomized for real container runtime, but here just use
|
||||
// fixed BuildContainerName() for easily making fake containers.
|
||||
containerID := BuildContainerName(config.Metadata, podSandboxID)
|
||||
createdAt := time.Now().Unix()
|
||||
createdState := runtimeapi.ContainerState_CONTAINER_CREATED
|
||||
imageRef := config.Image.GetImage()
|
||||
r.Containers[containerID] = &FakeContainer{
|
||||
ContainerStatus: runtimeapi.ContainerStatus{
|
||||
Id: &containerID,
|
||||
Metadata: config.Metadata,
|
||||
Image: config.Image,
|
||||
ImageRef: &imageRef,
|
||||
CreatedAt: &createdAt,
|
||||
State: &createdState,
|
||||
Labels: config.Labels,
|
||||
Annotations: config.Annotations,
|
||||
},
|
||||
SandboxID: podSandboxID,
|
||||
}
|
||||
|
||||
return containerID, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) StartContainer(containerID string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "StartContainer")
|
||||
|
||||
c, ok := r.Containers[containerID]
|
||||
if !ok {
|
||||
return fmt.Errorf("container %s not found", containerID)
|
||||
}
|
||||
|
||||
// Set container to running.
|
||||
startedAt := time.Now().Unix()
|
||||
runningState := runtimeapi.ContainerState_CONTAINER_RUNNING
|
||||
c.State = &runningState
|
||||
c.StartedAt = &startedAt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) StopContainer(containerID string, timeout int64) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "StopContainer")
|
||||
|
||||
c, ok := r.Containers[containerID]
|
||||
if !ok {
|
||||
return fmt.Errorf("container %q not found", containerID)
|
||||
}
|
||||
|
||||
// Set container to exited state.
|
||||
finishedAt := time.Now().Unix()
|
||||
exitedState := runtimeapi.ContainerState_CONTAINER_EXITED
|
||||
c.State = &exitedState
|
||||
c.FinishedAt = &finishedAt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) RemoveContainer(containerID string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "RemoveContainer")
|
||||
|
||||
// Remove the container
|
||||
delete(r.Containers, containerID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ListContainers")
|
||||
|
||||
result := make([]*runtimeapi.Container, 0)
|
||||
for _, s := range r.Containers {
|
||||
if filter != nil {
|
||||
if filter.Id != nil && filter.GetId() != s.GetId() {
|
||||
continue
|
||||
}
|
||||
if filter.PodSandboxId != nil && filter.GetPodSandboxId() != s.SandboxID {
|
||||
continue
|
||||
}
|
||||
if filter.State != nil && filter.GetState() != s.GetState() {
|
||||
continue
|
||||
}
|
||||
if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &runtimeapi.Container{
|
||||
Id: s.Id,
|
||||
CreatedAt: s.CreatedAt,
|
||||
PodSandboxId: &s.SandboxID,
|
||||
Metadata: s.Metadata,
|
||||
State: s.State,
|
||||
Image: s.Image,
|
||||
ImageRef: s.ImageRef,
|
||||
Labels: s.Labels,
|
||||
Annotations: s.Annotations,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ContainerStatus")
|
||||
|
||||
c, ok := r.Containers[containerID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("container %q not found", containerID)
|
||||
}
|
||||
|
||||
status := c.ContainerStatus
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "ExecSync")
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) Exec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "Exec")
|
||||
return &runtimeapi.ExecResponse{}, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Called = append(r.Called, "Attach")
|
||||
return &runtimeapi.AttachResponse{}, nil
|
||||
}
|
||||
|
||||
func (r *FakeRuntimeService) UpdateRuntimeConfig(runtimeCOnfig *runtimeapi.RuntimeConfig) error {
|
||||
return nil
|
||||
}
|
||||
46
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/utils.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubelet/api/testing/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
func BuildContainerName(metadata *runtimeapi.ContainerMetadata, sandboxID string) string {
|
||||
// include the sandbox ID to make the container ID unique.
|
||||
return fmt.Sprintf("%s_%s_%d", sandboxID, metadata.GetName(), metadata.GetAttempt())
|
||||
}
|
||||
|
||||
func BuildSandboxName(metadata *runtimeapi.PodSandboxMetadata) string {
|
||||
return fmt.Sprintf("%s_%s_%s_%d", metadata.GetName(), metadata.GetNamespace(), metadata.GetUid(), metadata.GetAttempt())
|
||||
}
|
||||
|
||||
func filterInLabels(filter, labels map[string]string) bool {
|
||||
for k, v := range filter {
|
||||
if value, ok := labels[k]; ok {
|
||||
if value != v {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
25
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/BUILD
generated
vendored
Normal file
25
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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 = [
|
||||
"api.pb.go",
|
||||
"constants.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/gogo/protobuf/proto",
|
||||
"//vendor:golang.org/x/net/context",
|
||||
"//vendor:google.golang.org/grpc",
|
||||
],
|
||||
)
|
||||
4062
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/api.pb.go
generated
vendored
Normal file
4062
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/api.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
885
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/api.proto
generated
vendored
Normal file
885
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/api.proto
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
27
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/constants.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime/constants.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
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 runtime
|
||||
|
||||
// This file contains all constants defined in CRI.
|
||||
|
||||
// Required runtime condition type.
|
||||
const (
|
||||
// RuntimeReady means the runtime is up and ready to accept basic containers.
|
||||
RuntimeReady = "RuntimeReady"
|
||||
// NetworkReady means the runtime network is up and ready to accept containers which require network.
|
||||
NetworkReady = "NetworkReady"
|
||||
)
|
||||
18
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats/BUILD
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
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 = ["types.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/apis/meta/v1:go_default_library"],
|
||||
)
|
||||
255
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats/types.go
generated
vendored
Normal file
255
vendor/k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
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 stats
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Summary is a top-level container for holding NodeStats and PodStats.
|
||||
type Summary struct {
|
||||
// Overall node stats.
|
||||
Node NodeStats `json:"node"`
|
||||
// Per-pod stats.
|
||||
Pods []PodStats `json:"pods"`
|
||||
}
|
||||
|
||||
// NodeStats holds node-level unprocessed sample stats.
|
||||
type NodeStats struct {
|
||||
// Reference to the measured Node.
|
||||
NodeName string `json:"nodeName"`
|
||||
// Stats of system daemons tracked as raw containers.
|
||||
// The system containers are named according to the SystemContainer* constants.
|
||||
// +optional
|
||||
SystemContainers []ContainerStats `json:"systemContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
|
||||
// The time at which data collection for the node-scoped (i.e. aggregate) stats was (re)started.
|
||||
StartTime metav1.Time `json:"startTime"`
|
||||
// Stats pertaining to CPU resources.
|
||||
// +optional
|
||||
CPU *CPUStats `json:"cpu,omitempty"`
|
||||
// Stats pertaining to memory (RAM) resources.
|
||||
// +optional
|
||||
Memory *MemoryStats `json:"memory,omitempty"`
|
||||
// Stats pertaining to network resources.
|
||||
// +optional
|
||||
Network *NetworkStats `json:"network,omitempty"`
|
||||
// Stats pertaining to total usage of filesystem resources on the rootfs used by node k8s components.
|
||||
// NodeFs.Used is the total bytes used on the filesystem.
|
||||
// +optional
|
||||
Fs *FsStats `json:"fs,omitempty"`
|
||||
// Stats about the underlying container runtime.
|
||||
// +optional
|
||||
Runtime *RuntimeStats `json:"runtime,omitempty"`
|
||||
}
|
||||
|
||||
// Stats pertaining to the underlying container runtime.
|
||||
type RuntimeStats struct {
|
||||
// Stats about the underlying filesystem where container images are stored.
|
||||
// This filesystem could be the same as the primary (root) filesystem.
|
||||
// Usage here refers to the total number of bytes occupied by images on the filesystem.
|
||||
// +optional
|
||||
ImageFs *FsStats `json:"imageFs,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// Container name for the system container tracking Kubelet usage.
|
||||
SystemContainerKubelet = "kubelet"
|
||||
// Container name for the system container tracking the runtime (e.g. docker or rkt) usage.
|
||||
SystemContainerRuntime = "runtime"
|
||||
// Container name for the system container tracking non-kubernetes processes.
|
||||
SystemContainerMisc = "misc"
|
||||
)
|
||||
|
||||
// PodStats holds pod-level unprocessed sample stats.
|
||||
type PodStats struct {
|
||||
// Reference to the measured Pod.
|
||||
PodRef PodReference `json:"podRef"`
|
||||
// The time at which data collection for the pod-scoped (e.g. network) stats was (re)started.
|
||||
StartTime metav1.Time `json:"startTime"`
|
||||
// Stats of containers in the measured pod.
|
||||
Containers []ContainerStats `json:"containers" patchStrategy:"merge" patchMergeKey:"name"`
|
||||
// Stats pertaining to network resources.
|
||||
// +optional
|
||||
Network *NetworkStats `json:"network,omitempty"`
|
||||
// Stats pertaining to volume usage of filesystem resources.
|
||||
// VolumeStats.UsedBytes is the number of bytes used by the Volume
|
||||
// +optional
|
||||
VolumeStats []VolumeStats `json:"volume,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
|
||||
}
|
||||
|
||||
// ContainerStats holds container-level unprocessed sample stats.
|
||||
type ContainerStats struct {
|
||||
// Reference to the measured container.
|
||||
Name string `json:"name"`
|
||||
// The time at which data collection for this container was (re)started.
|
||||
StartTime metav1.Time `json:"startTime"`
|
||||
// Stats pertaining to CPU resources.
|
||||
// +optional
|
||||
CPU *CPUStats `json:"cpu,omitempty"`
|
||||
// Stats pertaining to memory (RAM) resources.
|
||||
// +optional
|
||||
Memory *MemoryStats `json:"memory,omitempty"`
|
||||
// Stats pertaining to container rootfs usage of filesystem resources.
|
||||
// Rootfs.UsedBytes is the number of bytes used for the container write layer.
|
||||
// +optional
|
||||
Rootfs *FsStats `json:"rootfs,omitempty"`
|
||||
// Stats pertaining to container logs usage of filesystem resources.
|
||||
// Logs.UsedBytes is the number of bytes used for the container logs.
|
||||
// +optional
|
||||
Logs *FsStats `json:"logs,omitempty"`
|
||||
// User defined metrics that are exposed by containers in the pod. Typically, we expect only one container in the pod to be exposing user defined metrics. In the event of multiple containers exposing metrics, they will be combined here.
|
||||
UserDefinedMetrics []UserDefinedMetric `json:"userDefinedMetrics,omitmepty" patchStrategy:"merge" patchMergeKey:"name"`
|
||||
}
|
||||
|
||||
// PodReference contains enough information to locate the referenced pod.
|
||||
type PodReference struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
|
||||
// NetworkStats contains data about network resources.
|
||||
type NetworkStats struct {
|
||||
// The time at which these stats were updated.
|
||||
Time metav1.Time `json:"time"`
|
||||
// Cumulative count of bytes received.
|
||||
// +optional
|
||||
RxBytes *uint64 `json:"rxBytes,omitempty"`
|
||||
// Cumulative count of receive errors encountered.
|
||||
// +optional
|
||||
RxErrors *uint64 `json:"rxErrors,omitempty"`
|
||||
// Cumulative count of bytes transmitted.
|
||||
// +optional
|
||||
TxBytes *uint64 `json:"txBytes,omitempty"`
|
||||
// Cumulative count of transmit errors encountered.
|
||||
// +optional
|
||||
TxErrors *uint64 `json:"txErrors,omitempty"`
|
||||
}
|
||||
|
||||
// CPUStats contains data about CPU usage.
|
||||
type CPUStats struct {
|
||||
// The time at which these stats were updated.
|
||||
Time metav1.Time `json:"time"`
|
||||
// Total CPU usage (sum of all cores) averaged over the sample window.
|
||||
// The "core" unit can be interpreted as CPU core-nanoseconds per second.
|
||||
// +optional
|
||||
UsageNanoCores *uint64 `json:"usageNanoCores,omitempty"`
|
||||
// Cumulative CPU usage (sum of all cores) since object creation.
|
||||
// +optional
|
||||
UsageCoreNanoSeconds *uint64 `json:"usageCoreNanoSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// MemoryStats contains data about memory usage.
|
||||
type MemoryStats struct {
|
||||
// The time at which these stats were updated.
|
||||
Time metav1.Time `json:"time"`
|
||||
// Available memory for use. This is defined as the memory limit - workingSetBytes.
|
||||
// If memory limit is undefined, the available bytes is omitted.
|
||||
// +optional
|
||||
AvailableBytes *uint64 `json:"availableBytes,omitempty"`
|
||||
// Total memory in use. This includes all memory regardless of when it was accessed.
|
||||
// +optional
|
||||
UsageBytes *uint64 `json:"usageBytes,omitempty"`
|
||||
// The amount of working set memory. This includes recently accessed memory,
|
||||
// dirty memory, and kernel memory. WorkingSetBytes is <= UsageBytes
|
||||
// +optional
|
||||
WorkingSetBytes *uint64 `json:"workingSetBytes,omitempty"`
|
||||
// The amount of anonymous and swap cache memory (includes transparent
|
||||
// hugepages).
|
||||
// +optional
|
||||
RSSBytes *uint64 `json:"rssBytes,omitempty"`
|
||||
// Cumulative number of minor page faults.
|
||||
// +optional
|
||||
PageFaults *uint64 `json:"pageFaults,omitempty"`
|
||||
// Cumulative number of major page faults.
|
||||
// +optional
|
||||
MajorPageFaults *uint64 `json:"majorPageFaults,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeStats contains data about Volume filesystem usage.
|
||||
type VolumeStats struct {
|
||||
// Embedded FsStats
|
||||
FsStats
|
||||
// Name is the name given to the Volume
|
||||
// +optional
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// FsStats contains data about filesystem usage.
|
||||
type FsStats struct {
|
||||
// AvailableBytes represents the storage space available (bytes) for the filesystem.
|
||||
// +optional
|
||||
AvailableBytes *uint64 `json:"availableBytes,omitempty"`
|
||||
// CapacityBytes represents the total capacity (bytes) of the filesystems underlying storage.
|
||||
// +optional
|
||||
CapacityBytes *uint64 `json:"capacityBytes,omitempty"`
|
||||
// UsedBytes represents the bytes used for a specific task on the filesystem.
|
||||
// This may differ from the total bytes used on the filesystem and may not equal CapacityBytes - AvailableBytes.
|
||||
// e.g. For ContainerStats.Rootfs this is the bytes used by the container rootfs on the filesystem.
|
||||
// +optional
|
||||
UsedBytes *uint64 `json:"usedBytes,omitempty"`
|
||||
// InodesFree represents the free inodes in the filesystem.
|
||||
// +optional
|
||||
InodesFree *uint64 `json:"inodesFree,omitempty"`
|
||||
// Inodes represents the total inodes in the filesystem.
|
||||
// +optional
|
||||
Inodes *uint64 `json:"inodes,omitempty"`
|
||||
// InodesUsed represents the inodes used by the filesystem
|
||||
// This may not equal Inodes - InodesFree because this filesystem may share inodes with other "filesystems"
|
||||
// e.g. For ContainerStats.Rootfs, this is the inodes used only by that container, and does not count inodes used by other containers.
|
||||
InodesUsed *uint64 `json:"inodesUsed,omitempty"`
|
||||
}
|
||||
|
||||
// UserDefinedMetricType defines how the metric should be interpreted by the user.
|
||||
type UserDefinedMetricType string
|
||||
|
||||
const (
|
||||
// Instantaneous value. May increase or decrease.
|
||||
MetricGauge UserDefinedMetricType = "gauge"
|
||||
|
||||
// A counter-like value that is only expected to increase.
|
||||
MetricCumulative UserDefinedMetricType = "cumulative"
|
||||
|
||||
// Rate over a time period.
|
||||
MetricDelta UserDefinedMetricType = "delta"
|
||||
)
|
||||
|
||||
// UserDefinedMetricDescriptor contains metadata that describes a user defined metric.
|
||||
type UserDefinedMetricDescriptor struct {
|
||||
// The name of the metric.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type of the metric.
|
||||
Type UserDefinedMetricType `json:"type"`
|
||||
|
||||
// Display Units for the stats.
|
||||
Units string `json:"units"`
|
||||
|
||||
// Metadata labels associated with this metric.
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// UserDefinedMetric represents a metric defined and generate by users.
|
||||
type UserDefinedMetric struct {
|
||||
UserDefinedMetricDescriptor `json:",inline"`
|
||||
// The time at which these stats were updated.
|
||||
Time metav1.Time `json:"time"`
|
||||
// Value of the metric. Float64s have 53 bit precision.
|
||||
// We do not foresee any metrics exceeding that value.
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
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 = [
|
||||
"cadvisor_linux.go",
|
||||
"doc.go",
|
||||
"types.go",
|
||||
"util.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/google/cadvisor/cache/memory",
|
||||
"//vendor:github.com/google/cadvisor/container",
|
||||
"//vendor:github.com/google/cadvisor/events",
|
||||
"//vendor:github.com/google/cadvisor/fs",
|
||||
"//vendor:github.com/google/cadvisor/http",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/google/cadvisor/info/v2",
|
||||
"//vendor:github.com/google/cadvisor/manager",
|
||||
"//vendor:github.com/google/cadvisor/metrics",
|
||||
"//vendor:github.com/google/cadvisor/utils/sysfs",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["cadvisor_linux_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/google/cadvisor/metrics",
|
||||
],
|
||||
)
|
||||
228
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux.go
generated
vendored
Normal file
228
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
// +build cgo,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 cadvisor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/cache/memory"
|
||||
cadvisormetrics "github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorfs "github.com/google/cadvisor/fs"
|
||||
cadvisorhttp "github.com/google/cadvisor/http"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
"github.com/google/cadvisor/manager"
|
||||
"github.com/google/cadvisor/metrics"
|
||||
"github.com/google/cadvisor/utils/sysfs"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/util/runtime"
|
||||
)
|
||||
|
||||
type cadvisorClient struct {
|
||||
runtime string
|
||||
rootPath string
|
||||
manager.Manager
|
||||
}
|
||||
|
||||
var _ Interface = new(cadvisorClient)
|
||||
|
||||
// TODO(vmarmol): Make configurable.
|
||||
// The amount of time for which to keep stats in memory.
|
||||
const statsCacheDuration = 2 * time.Minute
|
||||
const maxHousekeepingInterval = 15 * time.Second
|
||||
const defaultHousekeepingInterval = 10 * time.Second
|
||||
const allowDynamicHousekeeping = true
|
||||
|
||||
func init() {
|
||||
// Override cAdvisor flag defaults.
|
||||
flagOverrides := map[string]string{
|
||||
// Override the default cAdvisor housekeeping interval.
|
||||
"housekeeping_interval": defaultHousekeepingInterval.String(),
|
||||
// Disable event storage by default.
|
||||
"event_storage_event_limit": "default=0",
|
||||
"event_storage_age_limit": "default=0",
|
||||
}
|
||||
for name, defaultValue := range flagOverrides {
|
||||
if f := flag.Lookup(name); f != nil {
|
||||
f.DefValue = defaultValue
|
||||
f.Value.Set(defaultValue)
|
||||
} else {
|
||||
glog.Errorf("Expected cAdvisor flag %q not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containerLabels(c *cadvisorapi.ContainerInfo) map[string]string {
|
||||
set := map[string]string{metrics.LabelID: c.Name}
|
||||
if len(c.Aliases) > 0 {
|
||||
set[metrics.LabelName] = c.Aliases[0]
|
||||
}
|
||||
if image := c.Spec.Image; len(image) > 0 {
|
||||
set[metrics.LabelImage] = image
|
||||
}
|
||||
if v, ok := c.Spec.Labels[types.KubernetesPodNameLabel]; ok {
|
||||
set["pod_name"] = v
|
||||
}
|
||||
if v, ok := c.Spec.Labels[types.KubernetesPodNamespaceLabel]; ok {
|
||||
set["namespace"] = v
|
||||
}
|
||||
if v, ok := c.Spec.Labels[types.KubernetesContainerNameLabel]; ok {
|
||||
set["container_name"] = v
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// New creates a cAdvisor and exports its API on the specified port if port > 0.
|
||||
func New(port uint, runtime string, rootPath string) (Interface, error) {
|
||||
sysFs, err := sysfs.NewRealSysFs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create and start the cAdvisor container manager.
|
||||
m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, maxHousekeepingInterval, allowDynamicHousekeeping, cadvisormetrics.MetricSet{cadvisormetrics.NetworkTcpUsageMetrics: struct{}{}}, http.DefaultClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cadvisorClient := &cadvisorClient{
|
||||
runtime: runtime,
|
||||
rootPath: rootPath,
|
||||
Manager: m,
|
||||
}
|
||||
|
||||
err = cadvisorClient.exportHTTP(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cadvisorClient, nil
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) Start() error {
|
||||
return cc.Manager.Start()
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) exportHTTP(port uint) error {
|
||||
// Register the handlers regardless as this registers the prometheus
|
||||
// collector properly.
|
||||
mux := http.NewServeMux()
|
||||
err := cadvisorhttp.RegisterHandlers(mux, cc, "", "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cadvisorhttp.RegisterPrometheusHandler(mux, cc, "/metrics", containerLabels)
|
||||
|
||||
// Only start the http server if port > 0
|
||||
if port > 0 {
|
||||
serv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Remove this when the cAdvisor port is once again free.
|
||||
// If export failed, retry in the background until we are able to bind.
|
||||
// This allows an existing cAdvisor to be killed before this one registers.
|
||||
go func() {
|
||||
defer runtime.HandleCrash()
|
||||
|
||||
err := serv.ListenAndServe()
|
||||
for err != nil {
|
||||
glog.Infof("Failed to register cAdvisor on port %d, retrying. Error: %v", port, err)
|
||||
time.Sleep(time.Minute)
|
||||
err = serv.ListenAndServe()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
|
||||
return cc.GetContainerInfo(name, req)
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
|
||||
return cc.GetContainerInfoV2(name, options)
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) VersionInfo() (*cadvisorapi.VersionInfo, error) {
|
||||
return cc.GetVersionInfo()
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
|
||||
infos, err := cc.SubcontainersInfo(name, req)
|
||||
if err != nil && len(infos) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]*cadvisorapi.ContainerInfo, len(infos))
|
||||
for _, info := range infos {
|
||||
result[info.Name] = info
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) MachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
return cc.GetMachineInfo()
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
var label string
|
||||
|
||||
switch cc.runtime {
|
||||
case "docker":
|
||||
label = cadvisorfs.LabelDockerImages
|
||||
case "rkt":
|
||||
label = cadvisorfs.LabelRktImages
|
||||
default:
|
||||
return cadvisorapiv2.FsInfo{}, fmt.Errorf("ImagesFsInfo: unknown runtime: %v", cc.runtime)
|
||||
}
|
||||
|
||||
return cc.getFsInfo(label)
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cc.GetDirFsInfo(cc.rootPath)
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) getFsInfo(label string) (cadvisorapiv2.FsInfo, error) {
|
||||
res, err := cc.GetFsInfo(label)
|
||||
if err != nil {
|
||||
return cadvisorapiv2.FsInfo{}, err
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return cadvisorapiv2.FsInfo{}, fmt.Errorf("failed to find information for the filesystem labeled %q", label)
|
||||
}
|
||||
// TODO(vmarmol): Handle this better when a label has more than one image filesystem.
|
||||
if len(res) > 1 {
|
||||
glog.Warningf("More than one filesystem labeled %q: %#v. Only using the first one", label, res)
|
||||
}
|
||||
|
||||
return res[0], nil
|
||||
}
|
||||
|
||||
func (cc *cadvisorClient) WatchEvents(request *events.Request) (*events.EventChannel, error) {
|
||||
return cc.WatchForEvents(request)
|
||||
}
|
||||
65
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux_test.go
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// +build cgo,linux
|
||||
|
||||
/*
|
||||
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 cadvisor
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/metrics"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
func TestContainerLabels(t *testing.T) {
|
||||
container := &info.ContainerInfo{
|
||||
ContainerReference: info.ContainerReference{
|
||||
Name: "/docker/f81ad5335d390944e454ea19ab0924037d57337c19731524ad96eb26e74b6c6d",
|
||||
Aliases: []string{"k8s_POD.639b2af2_foo-web-315473031-e40e2_foobar_a369ace2-5fa9-11e6-b10f-c81f66e5e84d_851a97fd"},
|
||||
},
|
||||
Spec: info.ContainerSpec{
|
||||
Image: "qux/foo:latest",
|
||||
Labels: map[string]string{
|
||||
"io.kubernetes.container.hash": "639b2af2",
|
||||
types.KubernetesContainerNameLabel: "POD",
|
||||
"io.kubernetes.container.restartCount": "0",
|
||||
"io.kubernetes.container.terminationMessagePath": "",
|
||||
types.KubernetesPodNameLabel: "foo-web-315473031-e40e2",
|
||||
types.KubernetesPodNamespaceLabel: "foobar",
|
||||
"io.kubernetes.pod.terminationGracePeriod": "30",
|
||||
types.KubernetesPodUIDLabel: "a369ace2-5fa9-11e6-b10f-c81f66e5e84d",
|
||||
},
|
||||
Envs: map[string]string{
|
||||
"foo+env": "prod",
|
||||
},
|
||||
},
|
||||
}
|
||||
want := map[string]string{
|
||||
metrics.LabelID: "/docker/f81ad5335d390944e454ea19ab0924037d57337c19731524ad96eb26e74b6c6d",
|
||||
metrics.LabelName: "k8s_POD.639b2af2_foo-web-315473031-e40e2_foobar_a369ace2-5fa9-11e6-b10f-c81f66e5e84d_851a97fd",
|
||||
metrics.LabelImage: "qux/foo:latest",
|
||||
"namespace": "foobar",
|
||||
"container_name": "POD",
|
||||
"pod_name": "foo-web-315473031-e40e2",
|
||||
}
|
||||
|
||||
if have := containerLabels(container); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
78
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_unsupported.go
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// +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 cadvisor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
)
|
||||
|
||||
type cadvisorUnsupported struct {
|
||||
}
|
||||
|
||||
var _ Interface = new(cadvisorUnsupported)
|
||||
|
||||
func New(port uint, runtime string, rootPath string) (Interface, error) {
|
||||
return &cadvisorUnsupported{}, nil
|
||||
}
|
||||
|
||||
var unsupportedErr = errors.New("cAdvisor is unsupported in this build")
|
||||
|
||||
func (cu *cadvisorUnsupported) Start() error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) DockerContainer(name string, req *cadvisorapi.ContainerInfoRequest) (cadvisorapi.ContainerInfo, error) {
|
||||
return cadvisorapi.ContainerInfo{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) MachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) VersionInfo() (*cadvisorapi.VersionInfo, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (cu *cadvisorUnsupported) WatchEvents(request *events.Request) (*events.EventChannel, error) {
|
||||
return nil, unsupportedErr
|
||||
}
|
||||
75
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_windows.go
generated
vendored
Normal file
75
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/cadvisor_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// +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 cadvisor
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
)
|
||||
|
||||
type cadvisorClient struct {
|
||||
}
|
||||
|
||||
var _ Interface = new(cadvisorClient)
|
||||
|
||||
// New creates a cAdvisor and exports its API on the specified port if port > 0.
|
||||
func New(port uint, runtime string, rootPath string) (Interface, error) {
|
||||
return &cadvisorClient{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) DockerContainer(name string, req *cadvisorapi.ContainerInfoRequest) (cadvisorapi.ContainerInfo, error) {
|
||||
return cadvisorapi.ContainerInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
|
||||
return &cadvisorapi.ContainerInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
|
||||
return make(map[string]cadvisorapiv2.ContainerInfo), nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) MachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
return &cadvisorapi.MachineInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) VersionInfo() (*cadvisorapi.VersionInfo, error) {
|
||||
return &cadvisorapi.VersionInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, nil
|
||||
}
|
||||
|
||||
func (cu *cadvisorClient) WatchEvents(request *events.Request) (*events.EventChannel, error) {
|
||||
return &events.EventChannel{}, nil
|
||||
}
|
||||
18
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Kubelet interactions with cAdvisor.
|
||||
package cadvisor // import "k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
27
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/BUILD
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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 = [
|
||||
"cadvisor_fake.go",
|
||||
"cadvisor_mock.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubelet/cadvisor:go_default_library",
|
||||
"//vendor:github.com/google/cadvisor/events",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/google/cadvisor/info/v2",
|
||||
"//vendor:github.com/stretchr/testify/mock",
|
||||
],
|
||||
)
|
||||
75
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_fake.go
generated
vendored
Normal file
75
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_fake.go
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
)
|
||||
|
||||
// Fake cAdvisor implementation.
|
||||
type Fake struct {
|
||||
}
|
||||
|
||||
var _ cadvisor.Interface = new(Fake)
|
||||
|
||||
func (c *Fake) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Fake) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
|
||||
return new(cadvisorapi.ContainerInfo), nil
|
||||
}
|
||||
|
||||
func (c *Fake) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
|
||||
return map[string]cadvisorapiv2.ContainerInfo{}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
|
||||
return map[string]*cadvisorapi.ContainerInfo{}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) DockerContainer(name string, req *cadvisorapi.ContainerInfoRequest) (cadvisorapi.ContainerInfo, error) {
|
||||
return cadvisorapi.ContainerInfo{}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) MachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
// Simulate a matchin with 1 core and 3.75GB of memory.
|
||||
// We set it to non-zero values to make non-zero-capacity machines in Kubemark.
|
||||
return &cadvisorapi.MachineInfo{
|
||||
NumCores: 1,
|
||||
MemoryCapacity: 4026531840,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) VersionInfo() (*cadvisorapi.VersionInfo, error) {
|
||||
return new(cadvisorapi.VersionInfo), nil
|
||||
}
|
||||
|
||||
func (c *Fake) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
return cadvisorapiv2.FsInfo{}, nil
|
||||
}
|
||||
|
||||
func (c *Fake) WatchEvents(request *events.Request) (*events.EventChannel, error) {
|
||||
return new(events.EventChannel), nil
|
||||
}
|
||||
85
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_mock.go
generated
vendored
Normal file
85
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_mock.go
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
)
|
||||
|
||||
type Mock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
var _ cadvisor.Interface = new(Mock)
|
||||
|
||||
func (c *Mock) Start() error {
|
||||
args := c.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
// ContainerInfo is a mock implementation of Interface.ContainerInfo.
|
||||
func (c *Mock) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
|
||||
args := c.Called(name, req)
|
||||
return args.Get(0).(*cadvisorapi.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
// ContainerInfoV2 is a mock implementation of Interface.ContainerInfoV2.
|
||||
func (c *Mock) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
|
||||
args := c.Called(name, options)
|
||||
return args.Get(0).(map[string]cadvisorapiv2.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *Mock) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
|
||||
args := c.Called(name, req)
|
||||
return args.Get(0).(map[string]*cadvisorapi.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
// DockerContainer is a mock implementation of Interface.DockerContainer.
|
||||
func (c *Mock) DockerContainer(name string, req *cadvisorapi.ContainerInfoRequest) (cadvisorapi.ContainerInfo, error) {
|
||||
args := c.Called(name, req)
|
||||
return args.Get(0).(cadvisorapi.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
// MachineInfo is a mock implementation of Interface.MachineInfo.
|
||||
func (c *Mock) MachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(*cadvisorapi.MachineInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *Mock) VersionInfo() (*cadvisorapi.VersionInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(*cadvisorapi.VersionInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *Mock) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(cadvisorapiv2.FsInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *Mock) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(cadvisorapiv2.FsInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *Mock) WatchEvents(request *events.Request) (*events.EventChannel, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(*events.EventChannel), args.Error(1)
|
||||
}
|
||||
44
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/types.go
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
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 cadvisor
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/events"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
)
|
||||
|
||||
// Interface is an abstract interface for testability. It abstracts the interface to cAdvisor.
|
||||
type Interface interface {
|
||||
Start() error
|
||||
DockerContainer(name string, req *cadvisorapi.ContainerInfoRequest) (cadvisorapi.ContainerInfo, error)
|
||||
ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error)
|
||||
ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error)
|
||||
SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error)
|
||||
MachineInfo() (*cadvisorapi.MachineInfo, error)
|
||||
|
||||
VersionInfo() (*cadvisorapi.VersionInfo, error)
|
||||
|
||||
// Returns usage information about the filesystem holding Docker images.
|
||||
ImagesFsInfo() (cadvisorapiv2.FsInfo, error)
|
||||
|
||||
// Returns usage information about the root filesystem.
|
||||
RootFsInfo() (cadvisorapiv2.FsInfo, error)
|
||||
|
||||
// Get events streamed through passedChannel that fit the request.
|
||||
WatchEvents(request *events.Request) (*events.EventChannel, error)
|
||||
}
|
||||
35
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/util.go
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/kubelet/cadvisor/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
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 cadvisor
|
||||
|
||||
import (
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
func CapacityFromMachineInfo(info *cadvisorapi.MachineInfo) v1.ResourceList {
|
||||
c := v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(
|
||||
int64(info.NumCores*1000),
|
||||
resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(
|
||||
int64(info.MemoryCapacity),
|
||||
resource.BinarySI),
|
||||
}
|
||||
return c
|
||||
}
|
||||
40
vendor/k8s.io/kubernetes/pkg/kubelet/client/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/kubelet/client/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
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 = ["kubelet_client.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/transport:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/net:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["kubelet_client_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = [
|
||||
"automanaged",
|
||||
"skip",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/client/clientset_generated/release_1_5/typed/core/v1:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
],
|
||||
)
|
||||
180
vendor/k8s.io/kubernetes/pkg/kubelet/client/kubelet_client.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/kubelet/client/kubelet_client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/transport"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
nodeutil "k8s.io/kubernetes/pkg/util/node"
|
||||
)
|
||||
|
||||
type KubeletClientConfig struct {
|
||||
// Default port - used if no information about Kubelet port can be found in Node.NodeStatus.DaemonEndpoints.
|
||||
Port uint
|
||||
EnableHttps bool
|
||||
|
||||
// PreferredAddressTypes - used to select an address from Node.NodeStatus.Addresses
|
||||
PreferredAddressTypes []string
|
||||
|
||||
// TLSClientConfig contains settings to enable transport layer security
|
||||
restclient.TLSClientConfig
|
||||
|
||||
// Server requires Bearer authentication
|
||||
BearerToken string
|
||||
|
||||
// HTTPTimeout is used by the client to timeout http requests to Kubelet.
|
||||
HTTPTimeout time.Duration
|
||||
|
||||
// Dial is a custom dialer used for the client
|
||||
Dial func(net, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// ConnectionInfo provides the information needed to connect to a kubelet
|
||||
type ConnectionInfo struct {
|
||||
Scheme string
|
||||
Hostname string
|
||||
Port string
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// ConnectionInfoGetter provides ConnectionInfo for the kubelet running on a named node
|
||||
type ConnectionInfoGetter interface {
|
||||
GetConnectionInfo(ctx api.Context, nodeName types.NodeName) (*ConnectionInfo, error)
|
||||
}
|
||||
|
||||
func MakeTransport(config *KubeletClientConfig) (http.RoundTripper, error) {
|
||||
tlsConfig, err := transport.TLSConfigFor(config.transportConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rt := http.DefaultTransport
|
||||
if config.Dial != nil || tlsConfig != nil {
|
||||
rt = utilnet.SetOldTransportDefaults(&http.Transport{
|
||||
Dial: config.Dial,
|
||||
TLSClientConfig: tlsConfig,
|
||||
})
|
||||
}
|
||||
|
||||
return transport.HTTPWrappersForConfig(config.transportConfig(), rt)
|
||||
}
|
||||
|
||||
// transportConfig converts a client config to an appropriate transport config.
|
||||
func (c *KubeletClientConfig) transportConfig() *transport.Config {
|
||||
cfg := &transport.Config{
|
||||
TLS: transport.TLSConfig{
|
||||
CAFile: c.CAFile,
|
||||
CAData: c.CAData,
|
||||
CertFile: c.CertFile,
|
||||
CertData: c.CertData,
|
||||
KeyFile: c.KeyFile,
|
||||
KeyData: c.KeyData,
|
||||
},
|
||||
BearerToken: c.BearerToken,
|
||||
}
|
||||
if c.EnableHttps && !cfg.HasCA() {
|
||||
cfg.TLS.Insecure = true
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// NodeGetter defines an interface for looking up a node by name
|
||||
type NodeGetter interface {
|
||||
Get(name string) (*v1.Node, error)
|
||||
}
|
||||
|
||||
// NodeGetterFunc allows implementing NodeGetter with a function
|
||||
type NodeGetterFunc func(name string) (*v1.Node, error)
|
||||
|
||||
func (f NodeGetterFunc) Get(name string) (*v1.Node, error) {
|
||||
return f(name)
|
||||
}
|
||||
|
||||
// NodeConnectionInfoGetter obtains connection info from the status of a Node API object
|
||||
type NodeConnectionInfoGetter struct {
|
||||
// nodes is used to look up Node objects
|
||||
nodes NodeGetter
|
||||
// scheme is the scheme to use to connect to all kubelets
|
||||
scheme string
|
||||
// defaultPort is the port to use if no Kubelet endpoint port is recorded in the node status
|
||||
defaultPort int
|
||||
// transport is the transport to use to send a request to all kubelets
|
||||
transport http.RoundTripper
|
||||
// preferredAddressTypes specifies the preferred order to use to find a node address
|
||||
preferredAddressTypes []v1.NodeAddressType
|
||||
}
|
||||
|
||||
func NewNodeConnectionInfoGetter(nodes NodeGetter, config KubeletClientConfig) (ConnectionInfoGetter, error) {
|
||||
scheme := "http"
|
||||
if config.EnableHttps {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
transport, err := MakeTransport(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
types := []v1.NodeAddressType{}
|
||||
for _, t := range config.PreferredAddressTypes {
|
||||
types = append(types, v1.NodeAddressType(t))
|
||||
}
|
||||
|
||||
return &NodeConnectionInfoGetter{
|
||||
nodes: nodes,
|
||||
scheme: scheme,
|
||||
defaultPort: int(config.Port),
|
||||
transport: transport,
|
||||
|
||||
preferredAddressTypes: types,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *NodeConnectionInfoGetter) GetConnectionInfo(ctx api.Context, nodeName types.NodeName) (*ConnectionInfo, error) {
|
||||
node, err := k.nodes.Get(string(nodeName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find a kubelet-reported address, using preferred address type
|
||||
host, err := nodeutil.GetPreferredNodeAddress(node, k.preferredAddressTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the kubelet-reported port, if present
|
||||
port := int(node.Status.DaemonEndpoints.KubeletEndpoint.Port)
|
||||
if port <= 0 {
|
||||
port = k.defaultPort
|
||||
}
|
||||
|
||||
return &ConnectionInfo{
|
||||
Scheme: k.scheme,
|
||||
Hostname: host,
|
||||
Port: strconv.Itoa(port),
|
||||
Transport: k.transport,
|
||||
}, nil
|
||||
}
|
||||
70
vendor/k8s.io/kubernetes/pkg/kubelet/client/kubelet_client_test.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/kubelet/client/kubelet_client_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
v1core "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/core/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
)
|
||||
|
||||
// Ensure a node client can be used as a NodeGetter.
|
||||
// This allows anyone with a node client to easily construct a NewNodeConnectionInfoGetter.
|
||||
var _ = NodeGetter(v1core.NodeInterface(nil))
|
||||
|
||||
func TestMakeTransportInvalid(t *testing.T) {
|
||||
config := &KubeletClientConfig{
|
||||
EnableHttps: true,
|
||||
//Invalid certificate and key path
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CertFile: "../../client/testdata/mycertinvalid.cer",
|
||||
KeyFile: "../../client/testdata/mycertinvalid.key",
|
||||
CAFile: "../../client/testdata/myCA.cer",
|
||||
},
|
||||
}
|
||||
|
||||
rt, err := MakeTransport(config)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error")
|
||||
}
|
||||
if rt != nil {
|
||||
t.Error("rt should be nil as we provided invalid cert file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeTransportValid(t *testing.T) {
|
||||
config := &KubeletClientConfig{
|
||||
Port: 1234,
|
||||
EnableHttps: true,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CertFile: "../../client/testdata/mycertvalid.cer",
|
||||
// TLS Configuration, only applies if EnableHttps is true.
|
||||
KeyFile: "../../client/testdata/mycertvalid.key",
|
||||
// TLS Configuration, only applies if EnableHttps is true.
|
||||
CAFile: "../../client/testdata/myCA.cer",
|
||||
},
|
||||
}
|
||||
|
||||
rt, err := MakeTransport(config)
|
||||
if err != nil {
|
||||
t.Errorf("Not expecting an error #%v", err)
|
||||
}
|
||||
if rt == nil {
|
||||
t.Error("rt should not be nil")
|
||||
}
|
||||
}
|
||||
67
vendor/k8s.io/kubernetes/pkg/kubelet/cm/BUILD
generated
vendored
Normal file
67
vendor/k8s.io/kubernetes/pkg/kubelet/cm/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
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 = [
|
||||
"cgroup_manager_linux.go",
|
||||
"container_manager.go",
|
||||
"container_manager_linux.go",
|
||||
"container_manager_stub.go",
|
||||
"helpers_linux.go",
|
||||
"pod_container_manager_linux.go",
|
||||
"pod_container_manager_stub.go",
|
||||
"types.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/kubelet/cadvisor:go_default_library",
|
||||
"//pkg/kubelet/cm/util:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/oom:go_default_library",
|
||||
"//pkg/util/procfs:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups/fs",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups/systemd",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/configs",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cgroup_manager_linux_test.go",
|
||||
"container_manager_linux_test.go",
|
||||
"helpers_linux_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
"//vendor:github.com/stretchr/testify/require",
|
||||
],
|
||||
)
|
||||
454
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go
generated
vendored
Normal file
454
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
cgroupsystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
// libcontainerCgroupManagerType defines how to interface with libcontainer
|
||||
type libcontainerCgroupManagerType string
|
||||
|
||||
const (
|
||||
// libcontainerCgroupfs means use libcontainer with cgroupfs
|
||||
libcontainerCgroupfs libcontainerCgroupManagerType = "cgroupfs"
|
||||
// libcontainerSystemd means use libcontainer with systemd
|
||||
libcontainerSystemd libcontainerCgroupManagerType = "systemd"
|
||||
)
|
||||
|
||||
// ConvertCgroupNameToSystemd converts the internal cgroup name to a systemd name.
|
||||
// For example, the name /Burstable/pod_123-456 becomes Burstable-pod_123_456.slice
|
||||
// If outputToCgroupFs is true, it expands the systemd name into the cgroupfs form.
|
||||
// For example, it will return /Burstable.slice/Burstable-pod_123_456.slice in above scenario.
|
||||
func ConvertCgroupNameToSystemd(cgroupName CgroupName, outputToCgroupFs bool) string {
|
||||
name := string(cgroupName)
|
||||
result := ""
|
||||
if name != "" && name != "/" {
|
||||
// systemd treats - as a step in the hierarchy, we convert all - to _
|
||||
name = strings.Replace(name, "-", "_", -1)
|
||||
parts := strings.Split(name, "/")
|
||||
for _, part := range parts {
|
||||
// ignore leading stuff for now
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
if len(result) > 0 {
|
||||
result = result + "-"
|
||||
}
|
||||
result = result + part
|
||||
}
|
||||
} else {
|
||||
// root converts to -
|
||||
result = "-"
|
||||
}
|
||||
// always have a .slice suffix
|
||||
result = result + ".slice"
|
||||
|
||||
// if the caller desired the result in cgroupfs format...
|
||||
if outputToCgroupFs {
|
||||
var err error
|
||||
result, err = cgroupsystemd.ExpandSlice(result)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error adapting cgroup name, input: %v, err: %v", name, err))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ConvertCgroupFsNameToSystemd converts an expanded cgroupfs name to its systemd name.
|
||||
// For example, it will convert test.slice/test-a.slice/test-a-b.slice to become test-a-b.slice
|
||||
// NOTE: this is public right now to allow its usage in dockermanager and dockershim, ideally both those
|
||||
// code areas could use something from libcontainer if we get this style function upstream.
|
||||
func ConvertCgroupFsNameToSystemd(cgroupfsName string) (string, error) {
|
||||
// TODO: see if libcontainer systemd implementation could use something similar, and if so, move
|
||||
// this function up to that library. At that time, it would most likely do validation specific to systemd
|
||||
// above and beyond the simple assumption here that the base of the path encodes the hierarchy
|
||||
// per systemd convention.
|
||||
return path.Base(cgroupfsName), nil
|
||||
}
|
||||
|
||||
// libcontainerAdapter provides a simplified interface to libcontainer based on libcontainer type.
|
||||
type libcontainerAdapter struct {
|
||||
// cgroupManagerType defines how to interface with libcontainer
|
||||
cgroupManagerType libcontainerCgroupManagerType
|
||||
}
|
||||
|
||||
// newLibcontainerAdapter returns a configured libcontainerAdapter for specified manager.
|
||||
// it does any initialization required by that manager to function.
|
||||
func newLibcontainerAdapter(cgroupManagerType libcontainerCgroupManagerType) *libcontainerAdapter {
|
||||
return &libcontainerAdapter{cgroupManagerType: cgroupManagerType}
|
||||
}
|
||||
|
||||
// newManager returns an implementation of cgroups.Manager
|
||||
func (l *libcontainerAdapter) newManager(cgroups *libcontainerconfigs.Cgroup, paths map[string]string) (libcontainercgroups.Manager, error) {
|
||||
switch l.cgroupManagerType {
|
||||
case libcontainerCgroupfs:
|
||||
return &cgroupfs.Manager{
|
||||
Cgroups: cgroups,
|
||||
Paths: paths,
|
||||
}, nil
|
||||
case libcontainerSystemd:
|
||||
// this means you asked systemd to manage cgroups, but systemd was not on the host, so all you can do is panic...
|
||||
if !cgroupsystemd.UseSystemd() {
|
||||
panic("systemd cgroup manager not available")
|
||||
}
|
||||
return &cgroupsystemd.Manager{
|
||||
Cgroups: cgroups,
|
||||
Paths: paths,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid cgroup manager configuration")
|
||||
}
|
||||
|
||||
func (l *libcontainerAdapter) revertName(name string) CgroupName {
|
||||
if l.cgroupManagerType != libcontainerSystemd {
|
||||
return CgroupName(name)
|
||||
}
|
||||
|
||||
driverName, err := ConvertCgroupFsNameToSystemd(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
driverName = strings.TrimSuffix(driverName, ".slice")
|
||||
driverName = strings.Replace(driverName, "_", "-", -1)
|
||||
return CgroupName(driverName)
|
||||
}
|
||||
|
||||
// adaptName converts a CgroupName identifer to a driver specific conversion value.
|
||||
// if outputToCgroupFs is true, the result is returned in the cgroupfs format rather than the driver specific form.
|
||||
func (l *libcontainerAdapter) adaptName(cgroupName CgroupName, outputToCgroupFs bool) string {
|
||||
if l.cgroupManagerType != libcontainerSystemd {
|
||||
name := string(cgroupName)
|
||||
return name
|
||||
}
|
||||
return ConvertCgroupNameToSystemd(cgroupName, outputToCgroupFs)
|
||||
}
|
||||
|
||||
// CgroupSubsystems holds information about the mounted cgroup subsytems
|
||||
type CgroupSubsystems struct {
|
||||
// Cgroup subsystem mounts.
|
||||
// e.g.: "/sys/fs/cgroup/cpu" -> ["cpu", "cpuacct"]
|
||||
Mounts []libcontainercgroups.Mount
|
||||
|
||||
// Cgroup subsystem to their mount location.
|
||||
// e.g.: "cpu" -> "/sys/fs/cgroup/cpu"
|
||||
MountPoints map[string]string
|
||||
}
|
||||
|
||||
// cgroupManagerImpl implements the CgroupManager interface.
|
||||
// Its a stateless object which can be used to
|
||||
// update,create or delete any number of cgroups
|
||||
// It uses the Libcontainer raw fs cgroup manager for cgroup management.
|
||||
type cgroupManagerImpl struct {
|
||||
// subsystems holds information about all the
|
||||
// mounted cgroup subsytems on the node
|
||||
subsystems *CgroupSubsystems
|
||||
// simplifies interaction with libcontainer and its cgroup managers
|
||||
adapter *libcontainerAdapter
|
||||
}
|
||||
|
||||
// Make sure that cgroupManagerImpl implements the CgroupManager interface
|
||||
var _ CgroupManager = &cgroupManagerImpl{}
|
||||
|
||||
// NewCgroupManager is a factory method that returns a CgroupManager
|
||||
func NewCgroupManager(cs *CgroupSubsystems, cgroupDriver string) CgroupManager {
|
||||
managerType := libcontainerCgroupfs
|
||||
if cgroupDriver == string(libcontainerSystemd) {
|
||||
managerType = libcontainerSystemd
|
||||
}
|
||||
return &cgroupManagerImpl{
|
||||
subsystems: cs,
|
||||
adapter: newLibcontainerAdapter(managerType),
|
||||
}
|
||||
}
|
||||
|
||||
// Name converts the cgroup to the driver specific value in cgroupfs form.
|
||||
func (m *cgroupManagerImpl) Name(name CgroupName) string {
|
||||
return m.adapter.adaptName(name, true)
|
||||
}
|
||||
|
||||
// CgroupName converts the literal cgroupfs name on the host to an internal identifier.
|
||||
func (m *cgroupManagerImpl) CgroupName(name string) CgroupName {
|
||||
return m.adapter.revertName(name)
|
||||
}
|
||||
|
||||
// buildCgroupPaths builds a path to each cgroup subsystem for the specified name.
|
||||
func (m *cgroupManagerImpl) buildCgroupPaths(name CgroupName) map[string]string {
|
||||
cgroupFsAdaptedName := m.Name(name)
|
||||
cgroupPaths := make(map[string]string, len(m.subsystems.MountPoints))
|
||||
for key, val := range m.subsystems.MountPoints {
|
||||
cgroupPaths[key] = path.Join(val, cgroupFsAdaptedName)
|
||||
}
|
||||
return cgroupPaths
|
||||
}
|
||||
|
||||
// Exists checks if all subsystem cgroups already exist
|
||||
func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
|
||||
// Get map of all cgroup paths on the system for the particular cgroup
|
||||
cgroupPaths := m.buildCgroupPaths(name)
|
||||
|
||||
// If even one cgroup path doesn't exist, then the cgroup doesn't exist.
|
||||
for _, path := range cgroupPaths {
|
||||
if !libcontainercgroups.PathExists(path) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy destroys the specified cgroup
|
||||
func (m *cgroupManagerImpl) Destroy(cgroupConfig *CgroupConfig) error {
|
||||
cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
|
||||
|
||||
// we take the location in traditional cgroupfs format.
|
||||
abstractCgroupFsName := string(cgroupConfig.Name)
|
||||
abstractParent := CgroupName(path.Dir(abstractCgroupFsName))
|
||||
abstractName := CgroupName(path.Base(abstractCgroupFsName))
|
||||
|
||||
driverParent := m.adapter.adaptName(abstractParent, false)
|
||||
driverName := m.adapter.adaptName(abstractName, false)
|
||||
|
||||
// this is an ugly abstraction bleed, but systemd cgroup driver requires full paths...
|
||||
if m.adapter.cgroupManagerType == libcontainerSystemd {
|
||||
driverName = m.adapter.adaptName(cgroupConfig.Name, false)
|
||||
}
|
||||
|
||||
// Initialize libcontainer's cgroup config with driver specific naming.
|
||||
libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
|
||||
Name: driverName,
|
||||
Parent: driverParent,
|
||||
}
|
||||
|
||||
manager, err := m.adapter.newManager(libcontainerCgroupConfig, cgroupPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete cgroups using libcontainers Managers Destroy() method
|
||||
if err = manager.Destroy(); err != nil {
|
||||
return fmt.Errorf("Unable to destroy cgroup paths for cgroup %v : %v", cgroupConfig.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type subsystem interface {
|
||||
// Name returns the name of the subsystem.
|
||||
Name() string
|
||||
// Set the cgroup represented by cgroup.
|
||||
Set(path string, cgroup *libcontainerconfigs.Cgroup) error
|
||||
}
|
||||
|
||||
// Cgroup subsystems we currently support
|
||||
var supportedSubsystems = []subsystem{
|
||||
&cgroupfs.MemoryGroup{},
|
||||
&cgroupfs.CpuGroup{},
|
||||
}
|
||||
|
||||
// setSupportedSubsytems sets cgroup resource limits only on the supported
|
||||
// subsytems. ie. cpu and memory. We don't use libcontainer's cgroup/fs/Set()
|
||||
// method as it doesn't allow us to skip updates on the devices cgroup
|
||||
// Allowing or denying all devices by writing 'a' to devices.allow or devices.deny is
|
||||
// not possible once the device cgroups has children. Once the pod level cgroup are
|
||||
// created under the QOS level cgroup we cannot update the QOS level device cgroup.
|
||||
// We would like to skip setting any values on the device cgroup in this case
|
||||
// but this is not possible with libcontainers Set() method
|
||||
// See https://github.com/opencontainers/runc/issues/932
|
||||
func setSupportedSubsytems(cgroupConfig *libcontainerconfigs.Cgroup) error {
|
||||
for _, sys := range supportedSubsystems {
|
||||
if _, ok := cgroupConfig.Paths[sys.Name()]; !ok {
|
||||
return fmt.Errorf("Failed to find subsytem mount for subsytem: %v", sys.Name())
|
||||
}
|
||||
if err := sys.Set(cgroupConfig.Paths[sys.Name()], cgroupConfig); err != nil {
|
||||
return fmt.Errorf("Failed to set config for supported subsystems : %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *cgroupManagerImpl) toResources(resourceConfig *ResourceConfig) *libcontainerconfigs.Resources {
|
||||
resources := &libcontainerconfigs.Resources{}
|
||||
if resourceConfig == nil {
|
||||
return resources
|
||||
}
|
||||
if resourceConfig.Memory != nil {
|
||||
resources.Memory = *resourceConfig.Memory
|
||||
}
|
||||
if resourceConfig.CpuShares != nil {
|
||||
resources.CpuShares = *resourceConfig.CpuShares
|
||||
}
|
||||
if resourceConfig.CpuQuota != nil {
|
||||
resources.CpuQuota = *resourceConfig.CpuQuota
|
||||
}
|
||||
if resourceConfig.CpuPeriod != nil {
|
||||
resources.CpuPeriod = *resourceConfig.CpuPeriod
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
// Update updates the cgroup with the specified Cgroup Configuration
|
||||
func (m *cgroupManagerImpl) Update(cgroupConfig *CgroupConfig) error {
|
||||
// Extract the cgroup resource parameters
|
||||
resourceConfig := cgroupConfig.ResourceParameters
|
||||
resources := m.toResources(resourceConfig)
|
||||
|
||||
cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
|
||||
|
||||
// we take the location in traditional cgroupfs format.
|
||||
abstractCgroupFsName := string(cgroupConfig.Name)
|
||||
abstractParent := CgroupName(path.Dir(abstractCgroupFsName))
|
||||
abstractName := CgroupName(path.Base(abstractCgroupFsName))
|
||||
|
||||
driverParent := m.adapter.adaptName(abstractParent, false)
|
||||
driverName := m.adapter.adaptName(abstractName, false)
|
||||
|
||||
// this is an ugly abstraction bleed, but systemd cgroup driver requires full paths...
|
||||
if m.adapter.cgroupManagerType == libcontainerSystemd {
|
||||
driverName = m.adapter.adaptName(cgroupConfig.Name, false)
|
||||
}
|
||||
|
||||
// Initialize libcontainer's cgroup config
|
||||
libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
|
||||
Name: driverName,
|
||||
Parent: driverParent,
|
||||
Resources: resources,
|
||||
Paths: cgroupPaths,
|
||||
}
|
||||
|
||||
if err := setSupportedSubsytems(libcontainerCgroupConfig); err != nil {
|
||||
return fmt.Errorf("failed to set supported cgroup subsystems for cgroup %v: %v", cgroupConfig.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create creates the specified cgroup
|
||||
func (m *cgroupManagerImpl) Create(cgroupConfig *CgroupConfig) error {
|
||||
|
||||
// we take the location in traditional cgroupfs format.
|
||||
abstractCgroupFsName := string(cgroupConfig.Name)
|
||||
abstractParent := CgroupName(path.Dir(abstractCgroupFsName))
|
||||
abstractName := CgroupName(path.Base(abstractCgroupFsName))
|
||||
|
||||
driverParent := m.adapter.adaptName(abstractParent, false)
|
||||
driverName := m.adapter.adaptName(abstractName, false)
|
||||
// this is an ugly abstraction bleed, but systemd cgroup driver requires full paths...
|
||||
if m.adapter.cgroupManagerType == libcontainerSystemd {
|
||||
driverName = m.adapter.adaptName(cgroupConfig.Name, false)
|
||||
}
|
||||
|
||||
resources := m.toResources(cgroupConfig.ResourceParameters)
|
||||
// Initialize libcontainer's cgroup config with driver specific naming.
|
||||
libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
|
||||
Name: driverName,
|
||||
Parent: driverParent,
|
||||
Resources: resources,
|
||||
}
|
||||
|
||||
// get the manager with the specified cgroup configuration
|
||||
manager, err := m.adapter.newManager(libcontainerCgroupConfig, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Apply(-1) is a hack to create the cgroup directories for each resource
|
||||
// subsystem. The function [cgroups.Manager.apply()] applies cgroup
|
||||
// configuration to the process with the specified pid.
|
||||
// It creates cgroup files for each subsytems and writes the pid
|
||||
// in the tasks file. We use the function to create all the required
|
||||
// cgroup files but not attach any "real" pid to the cgroup.
|
||||
if err := manager.Apply(-1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// it may confuse why we call set after we do apply, but the issue is that runc
|
||||
// follows a similar pattern. it's needed to ensure cpu quota is set properly.
|
||||
m.Update(cgroupConfig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scans through all subsytems to find pids associated with specified cgroup.
|
||||
func (m *cgroupManagerImpl) Pids(name CgroupName) []int {
|
||||
// we need the driver specific name
|
||||
cgroupFsName := m.Name(name)
|
||||
|
||||
// Get a list of processes that we need to kill
|
||||
pidsToKill := sets.NewInt()
|
||||
var pids []int
|
||||
for _, val := range m.subsystems.MountPoints {
|
||||
dir := path.Join(val, cgroupFsName)
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
// The subsystem pod cgroup is already deleted
|
||||
// do nothing, continue
|
||||
continue
|
||||
}
|
||||
// Get a list of pids that are still charged to the pod's cgroup
|
||||
pids, err = getCgroupProcs(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pidsToKill.Insert(pids...)
|
||||
|
||||
// WalkFunc which is called for each file and directory in the pod cgroup dir
|
||||
visitor := func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
pids, err = getCgroupProcs(path)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("cgroup manager encountered error getting procs for cgroup path %v", path)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
pidsToKill.Insert(pids...)
|
||||
return nil
|
||||
}
|
||||
// Walk through the pod cgroup directory to check if
|
||||
// container cgroups haven't been GCed yet. Get attached processes to
|
||||
// all such unwanted containers under the pod cgroup
|
||||
if err = filepath.Walk(dir, visitor); err != nil {
|
||||
glog.V(5).Infof("cgroup manager encountered error scanning pids for directory: %v", dir)
|
||||
}
|
||||
}
|
||||
return pidsToKill.List()
|
||||
}
|
||||
|
||||
// ReduceCPULimits reduces the cgroup's cpu shares to the lowest possible value
|
||||
func (m *cgroupManagerImpl) ReduceCPULimits(cgroupName CgroupName) error {
|
||||
// Set lowest possible CpuShares value for the cgroup
|
||||
minimumCPUShares := int64(MinShares)
|
||||
resources := &ResourceConfig{
|
||||
CpuShares: &minimumCPUShares,
|
||||
}
|
||||
containerConfig := &CgroupConfig{
|
||||
Name: cgroupName,
|
||||
ResourceParameters: resources,
|
||||
}
|
||||
return m.Update(containerConfig)
|
||||
}
|
||||
81
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_linux_test.go
generated
vendored
Normal file
81
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_linux_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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 cm
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLibcontainerAdapterAdaptToSystemd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "/",
|
||||
expected: "-.slice",
|
||||
},
|
||||
{
|
||||
input: "/Burstable",
|
||||
expected: "Burstable.slice",
|
||||
},
|
||||
{
|
||||
input: "/Burstable/pod_123",
|
||||
expected: "Burstable-pod_123.slice",
|
||||
},
|
||||
{
|
||||
input: "/BestEffort/pod_6c1a4e95-6bb6-11e6-bc26-28d2444e470d",
|
||||
expected: "BestEffort-pod_6c1a4e95_6bb6_11e6_bc26_28d2444e470d.slice",
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
f := newLibcontainerAdapter(libcontainerSystemd)
|
||||
if actual := f.adaptName(CgroupName(testCase.input), false); actual != testCase.expected {
|
||||
t.Errorf("Unexpected result, input: %v, expected: %v, actual: %v", testCase.input, testCase.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLibcontainerAdapterAdaptToSystemdAsCgroupFs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "/",
|
||||
expected: "/",
|
||||
},
|
||||
{
|
||||
input: "/Burstable",
|
||||
expected: "Burstable.slice/",
|
||||
},
|
||||
{
|
||||
input: "/Burstable/pod_123",
|
||||
expected: "Burstable.slice/Burstable-pod_123.slice/",
|
||||
},
|
||||
{
|
||||
input: "/BestEffort/pod_6c1a4e95-6bb6-11e6-bc26-28d2444e470d",
|
||||
expected: "BestEffort.slice/BestEffort-pod_6c1a4e95_6bb6_11e6_bc26_28d2444e470d.slice/",
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
f := newLibcontainerAdapter(libcontainerSystemd)
|
||||
if actual := f.adaptName(CgroupName(testCase.input), true); actual != testCase.expected {
|
||||
t.Errorf("Unexpected result, input: %v, expected: %v, actual: %v", testCase.input, testCase.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
75
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_unsupported.go
generated
vendored
Normal file
75
vendor/k8s.io/kubernetes/pkg/kubelet/cm/cgroup_manager_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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 cm
|
||||
|
||||
import "fmt"
|
||||
|
||||
type unsupportedCgroupManager struct{}
|
||||
|
||||
// Make sure that unsupportedCgroupManager implements the CgroupManager interface
|
||||
var _ CgroupManager = &unsupportedCgroupManager{}
|
||||
|
||||
type CgroupSubsystems struct {
|
||||
Mounts []interface{}
|
||||
MountPoints map[string]string
|
||||
}
|
||||
|
||||
func NewCgroupManager(_ interface{}) CgroupManager {
|
||||
return &unsupportedCgroupManager{}
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Name(_ CgroupName) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Exists(_ CgroupName) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Destroy(_ *CgroupConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Update(_ *CgroupConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Create(_ *CgroupConfig) error {
|
||||
return fmt.Errorf("Cgroup Manager is not supported in this build")
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) Pids(_ CgroupName) []int {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) CgroupName(name string) CgroupName {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *unsupportedCgroupManager) ReduceCPULimits(cgroupName CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConvertCgroupFsNameToSystemd(cgroupfsName string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func ConvertCgroupNameToSystemd(cgroupName CgroupName, outputToCgroupFs bool) string {
|
||||
return ""
|
||||
}
|
||||
64
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager.go
generated
vendored
Normal file
64
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import "k8s.io/kubernetes/pkg/api/v1"
|
||||
|
||||
// Manages the containers running on a machine.
|
||||
type ContainerManager interface {
|
||||
// Runs the container manager's housekeeping.
|
||||
// - Ensures that the Docker daemon is in a container.
|
||||
// - Creates the system container where all non-containerized processes run.
|
||||
Start(*v1.Node) error
|
||||
|
||||
// Returns resources allocated to system cgroups in the machine.
|
||||
// These cgroups include the system and Kubernetes services.
|
||||
SystemCgroupsLimit() v1.ResourceList
|
||||
|
||||
// Returns a NodeConfig that is being used by the container manager.
|
||||
GetNodeConfig() NodeConfig
|
||||
|
||||
// Returns internal Status.
|
||||
Status() Status
|
||||
|
||||
// NewPodContainerManager is a factory method which returns a podContainerManager object
|
||||
// Returns a noop implementation if qos cgroup hierarchy is not enabled
|
||||
NewPodContainerManager() PodContainerManager
|
||||
|
||||
// GetMountedSubsystems returns the mounted cgroup subsytems on the node
|
||||
GetMountedSubsystems() *CgroupSubsystems
|
||||
|
||||
// GetQOSContainersInfo returns the names of top level QoS containers
|
||||
GetQOSContainersInfo() QOSContainersInfo
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
RuntimeCgroupsName string
|
||||
SystemCgroupsName string
|
||||
KubeletCgroupsName string
|
||||
ContainerRuntime string
|
||||
CgroupsPerQOS bool
|
||||
CgroupRoot string
|
||||
CgroupDriver string
|
||||
ProtectKernelDefaults bool
|
||||
EnableCRI bool
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
// Any soft requirements that were unsatisfied.
|
||||
SoftRequirements error
|
||||
}
|
||||
813
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_linux.go
generated
vendored
Normal file
813
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_linux.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
180
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_linux_test.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_linux_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// +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 cm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
type fakeMountInterface struct {
|
||||
mountPoints []mount.MountPoint
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) Mount(source string, target string, fstype string, options []string) error {
|
||||
return fmt.Errorf("unsupported")
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) Unmount(target string) error {
|
||||
return fmt.Errorf("unsupported")
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) List() ([]mount.MountPoint, error) {
|
||||
return mi.mountPoints, nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return false, fmt.Errorf("unsupported")
|
||||
}
|
||||
func (mi *fakeMountInterface) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) DeviceOpened(pathname string) (bool, error) {
|
||||
for _, mp := range mi.mountPoints {
|
||||
if mp.Device == pathname {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func fakeContainerMgrMountInt() mount.Interface {
|
||||
return &fakeMountInterface{
|
||||
[]mount.MountPoint{
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuset"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpu"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuacct"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "memory"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCgroupMountValidationSuccess(t *testing.T) {
|
||||
f, err := validateSystemRequirements(fakeContainerMgrMountInt())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, f.cpuHardcapping, "cpu hardcapping is expected to be disabled")
|
||||
}
|
||||
|
||||
func TestCgroupMountValidationMemoryMissing(t *testing.T) {
|
||||
mountInt := &fakeMountInterface{
|
||||
[]mount.MountPoint{
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuset"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpu"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuacct"},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := validateSystemRequirements(mountInt)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
|
||||
mountInt := &fakeMountInterface{
|
||||
[]mount.MountPoint{
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuset", "memory"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpu"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuacct"},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := validateSystemRequirements(mountInt)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestSoftRequirementsValidationSuccess(t *testing.T) {
|
||||
req := require.New(t)
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
req.NoError(err)
|
||||
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_period_us"), []byte("0"), os.ModePerm))
|
||||
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_quota_us"), []byte("0"), os.ModePerm))
|
||||
mountInt := &fakeMountInterface{
|
||||
[]mount.MountPoint{
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuset"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpu"},
|
||||
Path: tempDir,
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuacct", "memory"},
|
||||
},
|
||||
},
|
||||
}
|
||||
f, err := validateSystemRequirements(mountInt)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, f.cpuHardcapping, "cpu hardcapping is expected to be enabled")
|
||||
}
|
||||
59
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_stub.go
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
type containerManagerStub struct{}
|
||||
|
||||
var _ ContainerManager = &containerManagerStub{}
|
||||
|
||||
func (cm *containerManagerStub) Start(_ *v1.Node) error {
|
||||
glog.V(2).Infof("Starting stub container manager")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) SystemCgroupsLimit() v1.ResourceList {
|
||||
return v1.ResourceList{}
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) GetNodeConfig() NodeConfig {
|
||||
return NodeConfig{}
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) GetMountedSubsystems() *CgroupSubsystems {
|
||||
return &CgroupSubsystems{}
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) GetQOSContainersInfo() QOSContainersInfo {
|
||||
return QOSContainersInfo{}
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) Status() Status {
|
||||
return Status{}
|
||||
}
|
||||
|
||||
func (cm *containerManagerStub) NewPodContainerManager() PodContainerManager {
|
||||
return &podContainerManagerStub{}
|
||||
}
|
||||
|
||||
func NewStubContainerManager() ContainerManager {
|
||||
return &containerManagerStub{}
|
||||
}
|
||||
64
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_unsupported.go
generated
vendored
Normal file
64
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// +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 cm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
type unsupportedContainerManager struct {
|
||||
}
|
||||
|
||||
var _ ContainerManager = &unsupportedContainerManager{}
|
||||
|
||||
func (unsupportedContainerManager) Start(_ *v1.Node) error {
|
||||
return fmt.Errorf("Container Manager is unsupported in this build")
|
||||
}
|
||||
|
||||
func (unsupportedContainerManager) SystemCgroupsLimit() v1.ResourceList {
|
||||
return v1.ResourceList{}
|
||||
}
|
||||
|
||||
func (unsupportedContainerManager) GetNodeConfig() NodeConfig {
|
||||
return NodeConfig{}
|
||||
}
|
||||
|
||||
func (unsupportedContainerManager) GetMountedSubsystems() *CgroupSubsystems {
|
||||
return &CgroupSubsystems{}
|
||||
}
|
||||
|
||||
func (unsupportedContainerManager) GetQOSContainersInfo() QOSContainersInfo {
|
||||
return QOSContainersInfo{}
|
||||
}
|
||||
|
||||
func (cm *unsupportedContainerManager) Status() Status {
|
||||
return Status{}
|
||||
}
|
||||
|
||||
func (cm *unsupportedContainerManager) NewPodContainerManager() PodContainerManager {
|
||||
return &unsupportedPodContainerManager{}
|
||||
}
|
||||
|
||||
func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig, failSwapOn bool) (ContainerManager, error) {
|
||||
return &unsupportedContainerManager{}, nil
|
||||
}
|
||||
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_unsupported_test.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_unsupported_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// +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 cm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
type fakeMountInterface struct {
|
||||
mountPoints []mount.MountPoint
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) Mount(source string, target string, fstype string, options []string) error {
|
||||
return fmt.Errorf("unsupported")
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) Unmount(target string) error {
|
||||
return fmt.Errorf("unsupported")
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) List() ([]mount.MountPoint, error) {
|
||||
return mi.mountPoints, nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return false, fmt.Errorf("unsupported")
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) DeviceOpened(pathname string) (bool, error) {
|
||||
for _, mp := range mi.mountPoints {
|
||||
if mp.Device == pathname {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (mi *fakeMountInterface) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func fakeContainerMgrMountInt() mount.Interface {
|
||||
return &fakeMountInterface{
|
||||
[]mount.MountPoint{
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuset"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpu"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "cpuacct"},
|
||||
},
|
||||
{
|
||||
Device: "cgroup",
|
||||
Type: "cgroup",
|
||||
Opts: []string{"rw", "relatime", "memory"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
42
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_windows.go
generated
vendored
Normal file
42
vendor/k8s.io/kubernetes/pkg/kubelet/cm/container_manager_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// +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 cm
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
type containerManagerImpl struct {
|
||||
containerManagerStub
|
||||
}
|
||||
|
||||
var _ ContainerManager = &containerManagerImpl{}
|
||||
|
||||
func (cm *containerManagerImpl) Start(_ *v1.Node) error {
|
||||
glog.V(2).Infof("Starting Windows stub container manager")
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.Interface, nodeConfig NodeConfig, failSwapOn bool) (ContainerManager, error) {
|
||||
return &containerManagerImpl{}, nil
|
||||
}
|
||||
184
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_linux.go
generated
vendored
Normal file
184
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
)
|
||||
|
||||
const (
|
||||
// Taken from lmctfy https://github.com/google/lmctfy/blob/master/lmctfy/controllers/cpu_controller.cc
|
||||
MinShares = 2
|
||||
SharesPerCPU = 1024
|
||||
MilliCPUToCPU = 1000
|
||||
|
||||
// 100000 is equivalent to 100ms
|
||||
QuotaPeriod = 100000
|
||||
MinQuotaPeriod = 1000
|
||||
)
|
||||
|
||||
// MilliCPUToQuota converts milliCPU to CFS quota and period values.
|
||||
func MilliCPUToQuota(milliCPU int64) (quota int64, period int64) {
|
||||
// CFS quota is measured in two values:
|
||||
// - cfs_period_us=100ms (the amount of time to measure usage across)
|
||||
// - cfs_quota=20ms (the amount of cpu time allowed to be used across a period)
|
||||
// so in the above example, you are limited to 20% of a single CPU
|
||||
// for multi-cpu environments, you just scale equivalent amounts
|
||||
|
||||
if milliCPU == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// we set the period to 100ms by default
|
||||
period = QuotaPeriod
|
||||
|
||||
// we then convert your milliCPU to a value normalized over a period
|
||||
quota = (milliCPU * QuotaPeriod) / MilliCPUToCPU
|
||||
|
||||
// quota needs to be a minimum of 1ms.
|
||||
if quota < MinQuotaPeriod {
|
||||
quota = MinQuotaPeriod
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MilliCPUToShares converts the milliCPU to CFS shares.
|
||||
func MilliCPUToShares(milliCPU int64) int64 {
|
||||
if milliCPU == 0 {
|
||||
// Docker converts zero milliCPU to unset, which maps to kernel default
|
||||
// for unset: 1024. Return 2 here to really match kernel default for
|
||||
// zero milliCPU.
|
||||
return MinShares
|
||||
}
|
||||
// Conceptually (milliCPU / milliCPUToCPU) * sharesPerCPU, but factored to improve rounding.
|
||||
shares := (milliCPU * SharesPerCPU) / MilliCPUToCPU
|
||||
if shares < MinShares {
|
||||
return MinShares
|
||||
}
|
||||
return shares
|
||||
}
|
||||
|
||||
// ResourceConfigForPod takes the input pod and outputs the cgroup resource config.
|
||||
func ResourceConfigForPod(pod *v1.Pod) *ResourceConfig {
|
||||
// sum requests and limits, track if limits were applied for each resource.
|
||||
cpuRequests := int64(0)
|
||||
cpuLimits := int64(0)
|
||||
memoryLimits := int64(0)
|
||||
memoryLimitsDeclared := true
|
||||
cpuLimitsDeclared := true
|
||||
for _, container := range pod.Spec.Containers {
|
||||
cpuRequests += container.Resources.Requests.Cpu().MilliValue()
|
||||
cpuLimits += container.Resources.Limits.Cpu().MilliValue()
|
||||
if container.Resources.Limits.Cpu().IsZero() {
|
||||
cpuLimitsDeclared = false
|
||||
}
|
||||
memoryLimits += container.Resources.Limits.Memory().Value()
|
||||
if container.Resources.Limits.Memory().IsZero() {
|
||||
memoryLimitsDeclared = false
|
||||
}
|
||||
}
|
||||
|
||||
// convert to CFS values
|
||||
cpuShares := MilliCPUToShares(cpuRequests)
|
||||
cpuQuota, cpuPeriod := MilliCPUToQuota(cpuLimits)
|
||||
|
||||
// determine the qos class
|
||||
qosClass := qos.GetPodQOS(pod)
|
||||
|
||||
// build the result
|
||||
result := &ResourceConfig{}
|
||||
if qosClass == qos.Guaranteed {
|
||||
result.CpuShares = &cpuShares
|
||||
result.CpuQuota = &cpuQuota
|
||||
result.CpuPeriod = &cpuPeriod
|
||||
result.Memory = &memoryLimits
|
||||
} else if qosClass == qos.Burstable {
|
||||
result.CpuShares = &cpuShares
|
||||
if cpuLimitsDeclared {
|
||||
result.CpuQuota = &cpuQuota
|
||||
result.CpuPeriod = &cpuPeriod
|
||||
}
|
||||
if memoryLimitsDeclared {
|
||||
result.Memory = &memoryLimits
|
||||
}
|
||||
} else {
|
||||
shares := int64(MinShares)
|
||||
result.CpuShares = &shares
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetCgroupSubsystems returns information about the mounted cgroup subsystems
|
||||
func GetCgroupSubsystems() (*CgroupSubsystems, error) {
|
||||
// get all cgroup mounts.
|
||||
allCgroups, err := libcontainercgroups.GetCgroupMounts(true)
|
||||
if err != nil {
|
||||
return &CgroupSubsystems{}, err
|
||||
}
|
||||
if len(allCgroups) == 0 {
|
||||
return &CgroupSubsystems{}, fmt.Errorf("failed to find cgroup mounts")
|
||||
}
|
||||
mountPoints := make(map[string]string, len(allCgroups))
|
||||
for _, mount := range allCgroups {
|
||||
for _, subsystem := range mount.Subsystems {
|
||||
mountPoints[subsystem] = mount.Mountpoint
|
||||
}
|
||||
}
|
||||
return &CgroupSubsystems{
|
||||
Mounts: allCgroups,
|
||||
MountPoints: mountPoints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getCgroupProcs takes a cgroup directory name as an argument
|
||||
// reads through the cgroup's procs file and returns a list of tgid's.
|
||||
// It returns an empty list if a procs file doesn't exists
|
||||
func getCgroupProcs(dir string) ([]int, error) {
|
||||
procsFile := filepath.Join(dir, "cgroup.procs")
|
||||
f, err := os.Open(procsFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// The procsFile does not exist, So no pids attached to this directory
|
||||
return []int{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
out := []int{}
|
||||
for s.Scan() {
|
||||
if t := s.Text(); t != "" {
|
||||
pid, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected line in %v; could not convert to pid: %v", procsFile, err)
|
||||
}
|
||||
out = append(out, pid)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
199
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_linux_test.go
generated
vendored
Normal file
199
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_linux_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// +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 cm
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
// getResourceList returns a ResourceList with the
|
||||
// specified cpu and memory resource values
|
||||
func getResourceList(cpu, memory string) v1.ResourceList {
|
||||
res := v1.ResourceList{}
|
||||
if cpu != "" {
|
||||
res[v1.ResourceCPU] = resource.MustParse(cpu)
|
||||
}
|
||||
if memory != "" {
|
||||
res[v1.ResourceMemory] = resource.MustParse(memory)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// getResourceRequirements returns a ResourceRequirements object
|
||||
func getResourceRequirements(requests, limits v1.ResourceList) v1.ResourceRequirements {
|
||||
res := v1.ResourceRequirements{}
|
||||
res.Requests = requests
|
||||
res.Limits = limits
|
||||
return res
|
||||
}
|
||||
|
||||
func TestResourceConfigForPod(t *testing.T) {
|
||||
minShares := int64(MinShares)
|
||||
burstableShares := MilliCPUToShares(100)
|
||||
memoryQuantity := resource.MustParse("200Mi")
|
||||
burstableMemory := memoryQuantity.Value()
|
||||
burstablePartialShares := MilliCPUToShares(200)
|
||||
burstableQuota, burstablePeriod := MilliCPUToQuota(200)
|
||||
guaranteedShares := MilliCPUToShares(100)
|
||||
guaranteedQuota, guaranteedPeriod := MilliCPUToQuota(100)
|
||||
memoryQuantity = resource.MustParse("100Mi")
|
||||
guaranteedMemory := memoryQuantity.Value()
|
||||
testCases := map[string]struct {
|
||||
pod *v1.Pod
|
||||
expected *ResourceConfig
|
||||
}{
|
||||
"besteffort": {
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &ResourceConfig{CpuShares: &minShares},
|
||||
},
|
||||
"burstable-no-limits": {
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &ResourceConfig{CpuShares: &burstableShares},
|
||||
},
|
||||
"burstable-with-limits": {
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &ResourceConfig{CpuShares: &burstableShares, CpuQuota: &burstableQuota, CpuPeriod: &burstablePeriod, Memory: &burstableMemory},
|
||||
},
|
||||
"burstable-partial-limits": {
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
|
||||
},
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &ResourceConfig{CpuShares: &burstablePartialShares},
|
||||
},
|
||||
"guaranteed": {
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &ResourceConfig{CpuShares: &guaranteedShares, CpuQuota: &guaranteedQuota, CpuPeriod: &guaranteedPeriod, Memory: &guaranteedMemory},
|
||||
},
|
||||
}
|
||||
for testName, testCase := range testCases {
|
||||
actual := ResourceConfigForPod(testCase.pod)
|
||||
if !reflect.DeepEqual(actual.CpuPeriod, testCase.expected.CpuPeriod) {
|
||||
t.Errorf("unexpected result, test: %v, cpu period not as expected", testName)
|
||||
}
|
||||
if !reflect.DeepEqual(actual.CpuQuota, testCase.expected.CpuQuota) {
|
||||
t.Errorf("unexpected result, test: %v, cpu quota not as expected", testName)
|
||||
}
|
||||
if !reflect.DeepEqual(actual.CpuShares, testCase.expected.CpuShares) {
|
||||
t.Errorf("unexpected result, test: %v, cpu shares not as expected", testName)
|
||||
}
|
||||
if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) {
|
||||
t.Errorf("unexpected result, test: %v, memory not as expected", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMilliCPUToQuota(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input int64
|
||||
quota int64
|
||||
period int64
|
||||
}{
|
||||
{
|
||||
input: int64(0),
|
||||
quota: int64(0),
|
||||
period: int64(0),
|
||||
},
|
||||
{
|
||||
input: int64(5),
|
||||
quota: int64(1000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(9),
|
||||
quota: int64(1000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(10),
|
||||
quota: int64(1000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(200),
|
||||
quota: int64(20000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(500),
|
||||
quota: int64(50000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(1000),
|
||||
quota: int64(100000),
|
||||
period: int64(100000),
|
||||
},
|
||||
{
|
||||
input: int64(1500),
|
||||
quota: int64(150000),
|
||||
period: int64(100000),
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
quota, period := MilliCPUToQuota(testCase.input)
|
||||
if quota != testCase.quota || period != testCase.period {
|
||||
t.Errorf("Input %v, expected quota %v period %v, but got quota %v period %v", testCase.input, testCase.quota, testCase.period, quota, period)
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_unsupported.go
generated
vendored
Normal file
54
vendor/k8s.io/kubernetes/pkg/kubelet/cm/helpers_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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 cm
|
||||
|
||||
import "k8s.io/kubernetes/pkg/api/v1"
|
||||
|
||||
const (
|
||||
MinShares = 0
|
||||
SharesPerCPU = 0
|
||||
MilliCPUToCPU = 0
|
||||
|
||||
QuotaPeriod = 0
|
||||
MinQuotaPeriod = 0
|
||||
)
|
||||
|
||||
// MilliCPUToQuota converts milliCPU to CFS quota and period values.
|
||||
func MilliCPUToQuota(milliCPU int64) (int64, int64) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// MilliCPUToShares converts the milliCPU to CFS shares.
|
||||
func MilliCPUToShares(milliCPU int64) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ResourceConfigForPod takes the input pod and outputs the cgroup resource config.
|
||||
func ResourceConfigForPod(pod *v1.Pod) *ResourceConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCgroupSubsystems returns information about the mounted cgroup subsystems
|
||||
func GetCgroupSubsystems() (*CgroupSubsystems, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getCgroupProcs(dir string) ([]int, error) {
|
||||
return nil, nil
|
||||
}
|
||||
263
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_linux.go
generated
vendored
Normal file
263
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
podCgroupNamePrefix = "pod"
|
||||
)
|
||||
|
||||
// podContainerManagerImpl implements podContainerManager interface.
|
||||
// It is the general implementation which allows pod level container
|
||||
// management if qos Cgroup is enabled.
|
||||
type podContainerManagerImpl struct {
|
||||
// nodeInfo stores information about the node resource capacity
|
||||
nodeInfo *v1.Node
|
||||
// qosContainersInfo hold absolute paths of the top level qos containers
|
||||
qosContainersInfo QOSContainersInfo
|
||||
// Stores the mounted cgroup subsystems
|
||||
subsystems *CgroupSubsystems
|
||||
// cgroupManager is the cgroup Manager Object responsible for managing all
|
||||
// pod cgroups.
|
||||
cgroupManager CgroupManager
|
||||
}
|
||||
|
||||
// Make sure that podContainerManagerImpl implements the PodContainerManager interface
|
||||
var _ PodContainerManager = &podContainerManagerImpl{}
|
||||
|
||||
// applyLimits sets pod cgroup resource limits
|
||||
// It also updates the resource limits on top level qos containers.
|
||||
func (m *podContainerManagerImpl) applyLimits(pod *v1.Pod) error {
|
||||
// This function will house the logic for setting the resource parameters
|
||||
// on the pod container config and updating top level qos container configs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists checks if the pod's cgroup already exists
|
||||
func (m *podContainerManagerImpl) Exists(pod *v1.Pod) bool {
|
||||
podContainerName, _ := m.GetPodContainerName(pod)
|
||||
return m.cgroupManager.Exists(podContainerName)
|
||||
}
|
||||
|
||||
// EnsureExists takes a pod as argument and makes sure that
|
||||
// pod cgroup exists if qos cgroup hierarchy flag is enabled.
|
||||
// If the pod level container doesen't already exist it is created.
|
||||
func (m *podContainerManagerImpl) EnsureExists(pod *v1.Pod) error {
|
||||
podContainerName, _ := m.GetPodContainerName(pod)
|
||||
// check if container already exist
|
||||
alreadyExists := m.Exists(pod)
|
||||
if !alreadyExists {
|
||||
// Create the pod container
|
||||
containerConfig := &CgroupConfig{
|
||||
Name: podContainerName,
|
||||
ResourceParameters: ResourceConfigForPod(pod),
|
||||
}
|
||||
if err := m.cgroupManager.Create(containerConfig); err != nil {
|
||||
return fmt.Errorf("failed to create container for %v : %v", podContainerName, err)
|
||||
}
|
||||
}
|
||||
// Apply appropriate resource limits on the pod container
|
||||
// Top level qos containers limits are not updated
|
||||
// until we figure how to maintain the desired state in the kubelet.
|
||||
// Because maintaining the desired state is difficult without checkpointing.
|
||||
if err := m.applyLimits(pod); err != nil {
|
||||
return fmt.Errorf("failed to apply resource limits on container for %v : %v", podContainerName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPodContainerName returns the CgroupName identifer, and its literal cgroupfs form on the host.
|
||||
func (m *podContainerManagerImpl) GetPodContainerName(pod *v1.Pod) (CgroupName, string) {
|
||||
podQOS := qos.GetPodQOS(pod)
|
||||
// Get the parent QOS container name
|
||||
var parentContainer string
|
||||
switch podQOS {
|
||||
case qos.Guaranteed:
|
||||
parentContainer = m.qosContainersInfo.Guaranteed
|
||||
case qos.Burstable:
|
||||
parentContainer = m.qosContainersInfo.Burstable
|
||||
case qos.BestEffort:
|
||||
parentContainer = m.qosContainersInfo.BestEffort
|
||||
}
|
||||
podContainer := podCgroupNamePrefix + string(pod.UID)
|
||||
|
||||
// Get the absolute path of the cgroup
|
||||
cgroupName := (CgroupName)(path.Join(parentContainer, podContainer))
|
||||
// Get the literal cgroupfs name
|
||||
cgroupfsName := m.cgroupManager.Name(cgroupName)
|
||||
|
||||
return cgroupName, cgroupfsName
|
||||
}
|
||||
|
||||
// Scan through the whole cgroup directory and kill all processes either
|
||||
// attached to the pod cgroup or to a container cgroup under the pod cgroup
|
||||
func (m *podContainerManagerImpl) tryKillingCgroupProcesses(podCgroup CgroupName) error {
|
||||
pidsToKill := m.cgroupManager.Pids(podCgroup)
|
||||
// No pids charged to the terminated pod cgroup return
|
||||
if len(pidsToKill) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errlist []error
|
||||
// os.Kill often errors out,
|
||||
// We try killing all the pids multiple times
|
||||
for i := 0; i < 5; i++ {
|
||||
if i != 0 {
|
||||
glog.V(3).Infof("Attempt %v failed to kill all unwanted process. Retyring", i)
|
||||
}
|
||||
errlist = []error{}
|
||||
for _, pid := range pidsToKill {
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
// Process not running anymore, do nothing
|
||||
continue
|
||||
}
|
||||
glog.V(3).Infof("Attempt to kill process with pid: %v", pid)
|
||||
if err := p.Kill(); err != nil {
|
||||
glog.V(3).Infof("failed to kill process with pid: %v", pid)
|
||||
errlist = append(errlist, err)
|
||||
}
|
||||
}
|
||||
if len(errlist) == 0 {
|
||||
glog.V(3).Infof("successfully killed all unwanted processes.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errlist)
|
||||
}
|
||||
|
||||
// Destroy destroys the pod container cgroup paths
|
||||
func (m *podContainerManagerImpl) Destroy(podCgroup CgroupName) error {
|
||||
// Try killing all the processes attached to the pod cgroup
|
||||
if err := m.tryKillingCgroupProcesses(podCgroup); err != nil {
|
||||
glog.V(3).Infof("failed to kill all the processes attached to the %v cgroups", podCgroup)
|
||||
return fmt.Errorf("failed to kill all the processes attached to the %v cgroups : %v", podCgroup, err)
|
||||
}
|
||||
|
||||
// Now its safe to remove the pod's cgroup
|
||||
containerConfig := &CgroupConfig{
|
||||
Name: podCgroup,
|
||||
ResourceParameters: &ResourceConfig{},
|
||||
}
|
||||
if err := m.cgroupManager.Destroy(containerConfig); err != nil {
|
||||
return fmt.Errorf("failed to delete cgroup paths for %v : %v", podCgroup, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReduceCPULimits reduces the CPU CFS values to the minimum amount of shares.
|
||||
func (m *podContainerManagerImpl) ReduceCPULimits(podCgroup CgroupName) error {
|
||||
return m.cgroupManager.ReduceCPULimits(podCgroup)
|
||||
}
|
||||
|
||||
// GetAllPodsFromCgroups scans through all the subsytems of pod cgroups
|
||||
// Get list of pods whose cgroup still exist on the cgroup mounts
|
||||
func (m *podContainerManagerImpl) GetAllPodsFromCgroups() (map[types.UID]CgroupName, error) {
|
||||
// Map for storing all the found pods on the disk
|
||||
foundPods := make(map[types.UID]CgroupName)
|
||||
qosContainersList := [3]string{m.qosContainersInfo.BestEffort, m.qosContainersInfo.Burstable, m.qosContainersInfo.Guaranteed}
|
||||
// Scan through all the subsystem mounts
|
||||
// and through each QoS cgroup directory for each subsystem mount
|
||||
// If a pod cgroup exists in even a single subsystem mount
|
||||
// we will attempt to delete it
|
||||
for _, val := range m.subsystems.MountPoints {
|
||||
for _, qosContainerName := range qosContainersList {
|
||||
// get the subsystems QoS cgroup absolute name
|
||||
qcConversion := m.cgroupManager.Name(CgroupName(qosContainerName))
|
||||
qc := path.Join(val, qcConversion)
|
||||
dirInfo, err := ioutil.ReadDir(qc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the cgroup directory %v : %v", qc, err)
|
||||
}
|
||||
for i := range dirInfo {
|
||||
// note: we do a contains check because on systemd, the literal cgroupfs name will prefix the qos as well.
|
||||
if dirInfo[i].IsDir() && strings.Contains(dirInfo[i].Name(), podCgroupNamePrefix) {
|
||||
// we need to convert the name to an internal identifier
|
||||
internalName := m.cgroupManager.CgroupName(dirInfo[i].Name())
|
||||
// we then split the name on the pod prefix to determine the uid
|
||||
parts := strings.Split(string(internalName), podCgroupNamePrefix)
|
||||
// the uid is missing, so we log the unexpected cgroup not of form pod<uid>
|
||||
if len(parts) != 2 {
|
||||
location := path.Join(qc, dirInfo[i].Name())
|
||||
glog.Errorf("pod cgroup manager ignoring unexpected cgroup %v because it is not a pod", location)
|
||||
continue
|
||||
}
|
||||
podUID := parts[1]
|
||||
// because the literal cgroupfs name could encode the qos tier (on systemd), we avoid double encoding
|
||||
// by just rebuilding the fully qualified CgroupName according to our internal convention.
|
||||
cgroupName := CgroupName(path.Join(qosContainerName, podCgroupNamePrefix+podUID))
|
||||
foundPods[types.UID(podUID)] = cgroupName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundPods, nil
|
||||
}
|
||||
|
||||
// podContainerManagerNoop implements podContainerManager interface.
|
||||
// It is a no-op implementation and basically does nothing
|
||||
// podContainerManagerNoop is used in case the QoS cgroup Hierarchy is not
|
||||
// enabled, so Exists() returns true always as the cgroupRoot
|
||||
// is expected to always exist.
|
||||
type podContainerManagerNoop struct {
|
||||
cgroupRoot CgroupName
|
||||
}
|
||||
|
||||
// Make sure that podContainerManagerStub implements the PodContainerManager interface
|
||||
var _ PodContainerManager = &podContainerManagerNoop{}
|
||||
|
||||
func (m *podContainerManagerNoop) Exists(_ *v1.Pod) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *podContainerManagerNoop) EnsureExists(_ *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerNoop) GetPodContainerName(_ *v1.Pod) (CgroupName, string) {
|
||||
return m.cgroupRoot, string(m.cgroupRoot)
|
||||
}
|
||||
|
||||
func (m *podContainerManagerNoop) GetPodContainerNameForDriver(_ *v1.Pod) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Destroy destroys the pod container cgroup paths
|
||||
func (m *podContainerManagerNoop) Destroy(_ CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerNoop) ReduceCPULimits(_ CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerNoop) GetAllPodsFromCgroups() (map[types.UID]CgroupName, error) {
|
||||
return nil, nil
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_stub.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type podContainerManagerStub struct {
|
||||
}
|
||||
|
||||
var _ PodContainerManager = &podContainerManagerStub{}
|
||||
|
||||
func (m *podContainerManagerStub) Exists(_ *v1.Pod) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *podContainerManagerStub) EnsureExists(_ *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerStub) GetPodContainerName(_ *v1.Pod) (CgroupName, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (m *podContainerManagerStub) Destroy(_ CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerStub) ReduceCPULimits(_ CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *podContainerManagerStub) GetAllPodsFromCgroups() (map[types.UID]CgroupName, error) {
|
||||
return nil, nil
|
||||
}
|
||||
53
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_unsupported.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/kubelet/cm/pod_container_manager_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type unsupportedPodContainerManager struct {
|
||||
}
|
||||
|
||||
var _ PodContainerManager = &unsupportedPodContainerManager{}
|
||||
|
||||
func (m *unsupportedPodContainerManager) Exists(_ *v1.Pod) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *unsupportedPodContainerManager) EnsureExists(_ *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *unsupportedPodContainerManager) GetPodContainerName(_ *v1.Pod) (CgroupName, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (m *unsupportedPodContainerManager) ReduceCPULimits(_ CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *unsupportedPodContainerManager) GetAllPodsFromCgroups() (map[types.UID]CgroupName, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *unsupportedPodContainerManager) Destroy(name CgroupName) error {
|
||||
return nil
|
||||
}
|
||||
107
vendor/k8s.io/kubernetes/pkg/kubelet/cm/types.go
generated
vendored
Normal file
107
vendor/k8s.io/kubernetes/pkg/kubelet/cm/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
// ResourceConfig holds information about all the supported cgroup resource parameters.
|
||||
type ResourceConfig struct {
|
||||
// Memory limit (in bytes).
|
||||
Memory *int64
|
||||
// CPU shares (relative weight vs. other containers).
|
||||
CpuShares *int64
|
||||
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
||||
CpuQuota *int64
|
||||
// CPU quota period.
|
||||
CpuPeriod *int64
|
||||
}
|
||||
|
||||
// CgroupName is the abstract name of a cgroup prior to any driver specific conversion.
|
||||
type CgroupName string
|
||||
|
||||
// CgroupConfig holds the cgroup configuration information.
|
||||
// This is common object which is used to specify
|
||||
// cgroup information to both systemd and raw cgroup fs
|
||||
// implementation of the Cgroup Manager interface.
|
||||
type CgroupConfig struct {
|
||||
// Fully qualified name prior to any driver specific conversions.
|
||||
Name CgroupName
|
||||
// ResourceParameters contains various cgroups settings to apply.
|
||||
ResourceParameters *ResourceConfig
|
||||
}
|
||||
|
||||
// CgroupManager allows for cgroup management.
|
||||
// Supports Cgroup Creation ,Deletion and Updates.
|
||||
type CgroupManager interface {
|
||||
// Create creates and applies the cgroup configurations on the cgroup.
|
||||
// It just creates the leaf cgroups.
|
||||
// It expects the parent cgroup to already exist.
|
||||
Create(*CgroupConfig) error
|
||||
// Destroy the cgroup.
|
||||
Destroy(*CgroupConfig) error
|
||||
// Update cgroup configuration.
|
||||
Update(*CgroupConfig) error
|
||||
// Exists checks if the cgroup already exists
|
||||
Exists(name CgroupName) bool
|
||||
// Name returns the literal cgroupfs name on the host after any driver specific conversions.
|
||||
// We would expect systemd implementation to make appropriate name conversion.
|
||||
// For example, if we pass /foo/bar
|
||||
// then systemd should convert the name to something like
|
||||
// foo.slice/foo-bar.slice
|
||||
Name(name CgroupName) string
|
||||
// CgroupName converts the literal cgroupfs name on the host to an internal identifier.
|
||||
CgroupName(name string) CgroupName
|
||||
// Pids scans through all subsytems to find pids associated with specified cgroup.
|
||||
Pids(name CgroupName) []int
|
||||
// ReduceCPULimits reduces the CPU CFS values to the minimum amount of shares.
|
||||
ReduceCPULimits(cgroupName CgroupName) error
|
||||
}
|
||||
|
||||
// QOSContainersInfo stores the names of containers per qos
|
||||
type QOSContainersInfo struct {
|
||||
Guaranteed string
|
||||
BestEffort string
|
||||
Burstable string
|
||||
}
|
||||
|
||||
// PodContainerManager stores and manages pod level containers
|
||||
// The Pod workers interact with the PodContainerManager to create and destroy
|
||||
// containers for the pod.
|
||||
type PodContainerManager interface {
|
||||
// GetPodContainerName returns the CgroupName identifer, and its literal cgroupfs form on the host.
|
||||
GetPodContainerName(*v1.Pod) (CgroupName, string)
|
||||
|
||||
// EnsureExists takes a pod as argument and makes sure that
|
||||
// pod cgroup exists if qos cgroup hierarchy flag is enabled.
|
||||
// If the pod cgroup doesen't already exist this method creates it.
|
||||
EnsureExists(*v1.Pod) error
|
||||
|
||||
// Exists returns true if the pod cgroup exists.
|
||||
Exists(*v1.Pod) bool
|
||||
|
||||
// Destroy takes a pod Cgroup name as argument and destroys the pod's container.
|
||||
Destroy(name CgroupName) error
|
||||
|
||||
// ReduceCPULimits reduces the CPU CFS values to the minimum amount of shares.
|
||||
ReduceCPULimits(name CgroupName) error
|
||||
|
||||
// GetAllPodsFromCgroups enumerates the set of pod uids to their associated cgroup based on state of cgroupfs system.
|
||||
GetAllPodsFromCgroups() (map[types.UID]CgroupName, error)
|
||||
}
|
||||
21
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/BUILD
generated
vendored
Normal file
21
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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 = ["cgroups_linux.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/utils",
|
||||
],
|
||||
)
|
||||
76
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/cgroups_linux.go
generated
vendored
Normal file
76
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/cgroups_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
libcontainerutils "github.com/opencontainers/runc/libcontainer/utils"
|
||||
)
|
||||
|
||||
// Forked from opencontainers/runc/libcontainer/cgroup/fs.Manager.GetPids()
|
||||
func GetPids(cgroupPath string) ([]int, error) {
|
||||
dir, err := getCgroupPath(cgroupPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return libcontainercgroups.GetPids(dir)
|
||||
}
|
||||
|
||||
// getCgroupPath gets the file path to the "devices" subsystem of the desired cgroup.
|
||||
// cgroupPath is the path in the cgroup hierarchy.
|
||||
func getCgroupPath(cgroupPath string) (string, error) {
|
||||
cgroupPath = libcontainerutils.CleanPath(cgroupPath)
|
||||
|
||||
mnt, root, err := libcontainercgroups.FindCgroupMountpointAndRoot("devices")
|
||||
// If we didn't mount the subsystem, there is no point we make the path.
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
|
||||
if filepath.IsAbs(cgroupPath) {
|
||||
// Sometimes subsystems can be mounted togethger as 'cpu,cpuacct'.
|
||||
return filepath.Join(root, mnt, cgroupPath), nil
|
||||
}
|
||||
|
||||
parentPath, err := getCgroupParentPath(mnt, root)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(parentPath, cgroupPath), nil
|
||||
}
|
||||
|
||||
// getCgroupParentPath gets the parent filepath to this cgroup, for resolving relative cgroup paths.
|
||||
func getCgroupParentPath(mountpoint, root string) (string, error) {
|
||||
// Use GetThisCgroupDir instead of GetInitCgroupDir, because the creating
|
||||
// process could in container and shared pid namespace with host, and
|
||||
// /proc/1/cgroup could point to whole other world of cgroups.
|
||||
initPath, err := libcontainercgroups.GetThisCgroupDir("devices")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// This is needed for nested containers, because in /proc/self/cgroup we
|
||||
// see paths from host, which don't exist in container.
|
||||
relDir, err := filepath.Rel(root, initPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(mountpoint, relDir), nil
|
||||
}
|
||||
23
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/cgroups_unsupported.go
generated
vendored
Normal file
23
vendor/k8s.io/kubernetes/pkg/kubelet/cm/util/cgroups_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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 util
|
||||
|
||||
func GetPids(cgroupPath string) ([]int, error) {
|
||||
return nil, nil
|
||||
}
|
||||
82
vendor/k8s.io/kubernetes/pkg/kubelet/config/BUILD
generated
vendored
Normal file
82
vendor/k8s.io/kubernetes/pkg/kubelet/config/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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 = [
|
||||
"apiserver.go",
|
||||
"common.go",
|
||||
"config.go",
|
||||
"doc.go",
|
||||
"file.go",
|
||||
"file_linux.go",
|
||||
"http.go",
|
||||
"sources.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/client/cache:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/fields:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/format:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/config:go_default_library",
|
||||
"//pkg/util/hash:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/validation/field:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/util/yaml:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:golang.org/x/exp/inotify",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"apiserver_test.go",
|
||||
"common_test.go",
|
||||
"config_test.go",
|
||||
"file_linux_test.go",
|
||||
"http_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/cache:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/conversion:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/testing:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
],
|
||||
)
|
||||
46
vendor/k8s.io/kubernetes/pkg/kubelet/config/apiserver.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubelet/config/apiserver.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Reads the pod configuration from the Kubernetes apiserver.
|
||||
package config
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
// NewSourceApiserver creates a config source that watches and pulls from the apiserver.
|
||||
func NewSourceApiserver(c *clientset.Clientset, nodeName types.NodeName, updates chan<- interface{}) {
|
||||
lw := cache.NewListWatchFromClient(c.Core().RESTClient(), "pods", v1.NamespaceAll, fields.OneTermEqualSelector(api.PodHostField, string(nodeName)))
|
||||
newSourceApiserverFromLW(lw, updates)
|
||||
}
|
||||
|
||||
// newSourceApiserverFromLW holds creates a config source that watches and pulls from the apiserver.
|
||||
func newSourceApiserverFromLW(lw cache.ListerWatcher, updates chan<- interface{}) {
|
||||
send := func(objs []interface{}) {
|
||||
var pods []*v1.Pod
|
||||
for _, o := range objs {
|
||||
pods = append(pods, o.(*v1.Pod))
|
||||
}
|
||||
updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source: kubetypes.ApiserverSource}
|
||||
}
|
||||
cache.NewReflector(lw, &v1.Pod{}, cache.NewUndeltaStore(send, cache.MetaNamespaceKeyFunc), 0).Run()
|
||||
}
|
||||
193
vendor/k8s.io/kubernetes/pkg/kubelet/config/apiserver_test.go
generated
vendored
Normal file
193
vendor/k8s.io/kubernetes/pkg/kubelet/config/apiserver_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
type fakePodLW struct {
|
||||
listResp runtime.Object
|
||||
watchResp watch.Interface
|
||||
}
|
||||
|
||||
func (lw fakePodLW) List(options v1.ListOptions) (runtime.Object, error) {
|
||||
return lw.listResp, nil
|
||||
}
|
||||
|
||||
func (lw fakePodLW) Watch(options v1.ListOptions) (watch.Interface, error) {
|
||||
return lw.watchResp, nil
|
||||
}
|
||||
|
||||
var _ cache.ListerWatcher = fakePodLW{}
|
||||
|
||||
func TestNewSourceApiserver_UpdatesAndMultiplePods(t *testing.T) {
|
||||
pod1v1 := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "p"},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/one"}}}}
|
||||
pod1v2 := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "p"},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/two"}}}}
|
||||
pod2 := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "q"},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/blah"}}}}
|
||||
|
||||
// Setup fake api client.
|
||||
fakeWatch := watch.NewFake()
|
||||
lw := fakePodLW{
|
||||
listResp: &v1.PodList{Items: []v1.Pod{*pod1v1}},
|
||||
watchResp: fakeWatch,
|
||||
}
|
||||
|
||||
ch := make(chan interface{})
|
||||
|
||||
newSourceApiserverFromLW(lw, ch)
|
||||
|
||||
got, ok := <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
expected := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v1)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
t.Errorf("Expected %#v; Got %#v", expected, update)
|
||||
}
|
||||
|
||||
// Add another pod
|
||||
fakeWatch.Add(pod2)
|
||||
got, ok = <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update = got.(kubetypes.PodUpdate)
|
||||
// Could be sorted either of these two ways:
|
||||
expectedA := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v1, pod2)
|
||||
expectedB := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2, pod1v1)
|
||||
|
||||
if !api.Semantic.DeepEqual(expectedA, update) && !api.Semantic.DeepEqual(expectedB, update) {
|
||||
t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, update)
|
||||
}
|
||||
|
||||
// Modify pod1
|
||||
fakeWatch.Modify(pod1v2)
|
||||
got, ok = <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update = got.(kubetypes.PodUpdate)
|
||||
expectedA = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v2, pod2)
|
||||
expectedB = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2, pod1v2)
|
||||
|
||||
if !api.Semantic.DeepEqual(expectedA, update) && !api.Semantic.DeepEqual(expectedB, update) {
|
||||
t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, update)
|
||||
}
|
||||
|
||||
// Delete pod1
|
||||
fakeWatch.Delete(pod1v2)
|
||||
got, ok = <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update = got.(kubetypes.PodUpdate)
|
||||
expected = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
// Delete pod2
|
||||
fakeWatch.Delete(pod2)
|
||||
got, ok = <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update = got.(kubetypes.PodUpdate)
|
||||
expected = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSourceApiserver_TwoNamespacesSameName(t *testing.T) {
|
||||
pod1 := v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "p", Namespace: "one"},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/one"}}}}
|
||||
pod2 := v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "p", Namespace: "two"},
|
||||
Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/blah"}}}}
|
||||
|
||||
// Setup fake api client.
|
||||
fakeWatch := watch.NewFake()
|
||||
lw := fakePodLW{
|
||||
listResp: &v1.PodList{Items: []v1.Pod{pod1, pod2}},
|
||||
watchResp: fakeWatch,
|
||||
}
|
||||
|
||||
ch := make(chan interface{})
|
||||
|
||||
newSourceApiserverFromLW(lw, ch)
|
||||
|
||||
got, ok := <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
// Make sure that we get both pods. Catches bug #2294.
|
||||
if !(len(update.Pods) == 2) {
|
||||
t.Errorf("Expected %d, Got %d", 2, len(update.Pods))
|
||||
}
|
||||
|
||||
// Delete pod1
|
||||
fakeWatch.Delete(&pod1)
|
||||
got, ok = <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update = got.(kubetypes.PodUpdate)
|
||||
if !(len(update.Pods) == 1) {
|
||||
t.Errorf("Expected %d, Got %d", 1, len(update.Pods))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSourceApiserverInitialEmptySendsEmptyPodUpdate(t *testing.T) {
|
||||
// Setup fake api client.
|
||||
fakeWatch := watch.NewFake()
|
||||
lw := fakePodLW{
|
||||
listResp: &v1.PodList{Items: []v1.Pod{}},
|
||||
watchResp: fakeWatch,
|
||||
}
|
||||
|
||||
ch := make(chan interface{})
|
||||
|
||||
newSourceApiserverFromLW(lw, ch)
|
||||
|
||||
got, ok := <-ch
|
||||
if !ok {
|
||||
t.Errorf("Unable to read from channel when expected")
|
||||
}
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
expected := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
t.Errorf("Expected %#v; Got %#v", expected, update)
|
||||
}
|
||||
}
|
||||
150
vendor/k8s.io/kubernetes/pkg/kubelet/config/common.go
generated
vendored
Normal file
150
vendor/k8s.io/kubernetes/pkg/kubelet/config/common.go
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Common logic used by both http and file channels.
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/hash"
|
||||
utilyaml "k8s.io/kubernetes/pkg/util/yaml"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Generate a pod name that is unique among nodes by appending the nodeName.
|
||||
func generatePodName(name string, nodeName types.NodeName) string {
|
||||
return fmt.Sprintf("%s-%s", name, nodeName)
|
||||
}
|
||||
|
||||
func applyDefaults(pod *api.Pod, source string, isFile bool, nodeName types.NodeName) error {
|
||||
if len(pod.UID) == 0 {
|
||||
hasher := md5.New()
|
||||
if isFile {
|
||||
fmt.Fprintf(hasher, "host:%s", nodeName)
|
||||
fmt.Fprintf(hasher, "file:%s", source)
|
||||
} else {
|
||||
fmt.Fprintf(hasher, "url:%s", source)
|
||||
}
|
||||
hash.DeepHashObject(hasher, pod)
|
||||
pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
|
||||
glog.V(5).Infof("Generated UID %q pod %q from %s", pod.UID, pod.Name, source)
|
||||
}
|
||||
|
||||
pod.Name = generatePodName(pod.Name, nodeName)
|
||||
glog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, source)
|
||||
|
||||
if pod.Namespace == "" {
|
||||
pod.Namespace = kubetypes.NamespaceDefault
|
||||
}
|
||||
glog.V(5).Infof("Using namespace %q for pod %q from %s", pod.Namespace, pod.Name, source)
|
||||
|
||||
// Set the Host field to indicate this pod is scheduled on the current node.
|
||||
pod.Spec.NodeName = string(nodeName)
|
||||
|
||||
pod.ObjectMeta.SelfLink = getSelfLink(pod.Name, pod.Namespace)
|
||||
|
||||
if pod.Annotations == nil {
|
||||
pod.Annotations = make(map[string]string)
|
||||
}
|
||||
// The generated UID is the hash of the file.
|
||||
pod.Annotations[kubetypes.ConfigHashAnnotationKey] = string(pod.UID)
|
||||
|
||||
// Set the default status to pending.
|
||||
pod.Status.Phase = api.PodPending
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSelfLink(name, namespace string) string {
|
||||
var selfLink string
|
||||
if len(namespace) == 0 {
|
||||
namespace = api.NamespaceDefault
|
||||
}
|
||||
selfLink = fmt.Sprintf("/api/"+registered.GroupOrDie(api.GroupName).GroupVersion.Version+"/pods/namespaces/%s/%s", name, namespace)
|
||||
return selfLink
|
||||
}
|
||||
|
||||
type defaultFunc func(pod *api.Pod) error
|
||||
|
||||
func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v1.Pod, err error) {
|
||||
// JSON is valid YAML, so this should work for everything.
|
||||
json, err := utilyaml.ToJSON(data)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), json)
|
||||
if err != nil {
|
||||
return false, pod, err
|
||||
}
|
||||
// Check whether the object could be converted to single pod.
|
||||
if _, ok := obj.(*api.Pod); !ok {
|
||||
err = fmt.Errorf("invalid pod: %#v", obj)
|
||||
return false, pod, err
|
||||
}
|
||||
newPod := obj.(*api.Pod)
|
||||
// Apply default values and validate the pod.
|
||||
if err = defaultFn(newPod); err != nil {
|
||||
return true, pod, err
|
||||
}
|
||||
if errs := validation.ValidatePod(newPod); len(errs) > 0 {
|
||||
err = fmt.Errorf("invalid pod: %v", errs)
|
||||
return true, pod, err
|
||||
}
|
||||
v1Pod := &v1.Pod{}
|
||||
if err := v1.Convert_api_Pod_To_v1_Pod(newPod, v1Pod, nil); err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
return true, v1Pod, nil
|
||||
}
|
||||
|
||||
func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) {
|
||||
obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return false, pods, err
|
||||
}
|
||||
// Check whether the object could be converted to list of pods.
|
||||
if _, ok := obj.(*api.PodList); !ok {
|
||||
err = fmt.Errorf("invalid pods list: %#v", obj)
|
||||
return false, pods, err
|
||||
}
|
||||
newPods := obj.(*api.PodList)
|
||||
// Apply default values and validate pods.
|
||||
for i := range newPods.Items {
|
||||
newPod := &newPods.Items[i]
|
||||
if err = defaultFn(newPod); err != nil {
|
||||
return true, pods, err
|
||||
}
|
||||
if errs := validation.ValidatePod(newPod); len(errs) > 0 {
|
||||
err = fmt.Errorf("invalid pod: %v", errs)
|
||||
return true, pods, err
|
||||
}
|
||||
}
|
||||
v1Pods := &v1.PodList{}
|
||||
if err := v1.Convert_api_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil {
|
||||
return true, pods, err
|
||||
}
|
||||
return true, *v1Pods, err
|
||||
}
|
||||
158
vendor/k8s.io/kubernetes/pkg/kubelet/config/common_test.go
generated
vendored
Normal file
158
vendor/k8s.io/kubernetes/pkg/kubelet/config/common_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
)
|
||||
|
||||
func noDefault(*api.Pod) error { return nil }
|
||||
|
||||
func TestDecodeSinglePod(t *testing.T) {
|
||||
grace := int64(30)
|
||||
pod := &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "test",
|
||||
UID: "12345",
|
||||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
Containers: []v1.Container{{
|
||||
Name: "image",
|
||||
Image: "test/image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
}
|
||||
json, err := runtime.Encode(testapi.Default.Codec(), pod)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
parsed, podOut, err := tryDecodeSinglePod(json, noDefault)
|
||||
if !parsed {
|
||||
t.Errorf("expected to have parsed file: (%s)", string(json))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v (%s)", err, string(json))
|
||||
}
|
||||
if !reflect.DeepEqual(pod, podOut) {
|
||||
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, podOut, string(json))
|
||||
}
|
||||
|
||||
for _, gv := range registered.EnabledVersionsForGroup(v1.GroupName) {
|
||||
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/yaml")
|
||||
encoder := api.Codecs.EncoderForVersion(info.Serializer, gv)
|
||||
yaml, err := runtime.Encode(encoder, pod)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
parsed, podOut, err = tryDecodeSinglePod(yaml, noDefault)
|
||||
if !parsed {
|
||||
t.Errorf("expected to have parsed file: (%s)", string(yaml))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v (%s)", err, string(yaml))
|
||||
}
|
||||
if !reflect.DeepEqual(pod, podOut) {
|
||||
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, podOut, string(yaml))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodePodList(t *testing.T) {
|
||||
grace := int64(30)
|
||||
pod := &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "test",
|
||||
UID: "12345",
|
||||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
Containers: []v1.Container{{
|
||||
Name: "image",
|
||||
Image: "test/image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
}
|
||||
podList := &v1.PodList{
|
||||
Items: []v1.Pod{*pod},
|
||||
}
|
||||
json, err := runtime.Encode(testapi.Default.Codec(), podList)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
parsed, podListOut, err := tryDecodePodList(json, noDefault)
|
||||
if !parsed {
|
||||
t.Errorf("expected to have parsed file: (%s)", string(json))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v (%s)", err, string(json))
|
||||
}
|
||||
if !reflect.DeepEqual(podList, &podListOut) {
|
||||
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", podList, &podListOut, string(json))
|
||||
}
|
||||
|
||||
for _, gv := range registered.EnabledVersionsForGroup(v1.GroupName) {
|
||||
info, _ := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), "application/yaml")
|
||||
encoder := api.Codecs.EncoderForVersion(info.Serializer, gv)
|
||||
yaml, err := runtime.Encode(encoder, podList)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
parsed, podListOut, err = tryDecodePodList(yaml, noDefault)
|
||||
if !parsed {
|
||||
t.Errorf("expected to have parsed file: (%s): %v", string(yaml), err)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v (%s)", err, string(yaml))
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(podList, &podListOut) {
|
||||
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, &podListOut, string(yaml))
|
||||
}
|
||||
}
|
||||
}
|
||||
541
vendor/k8s.io/kubernetes/pkg/kubelet/config/config.go
generated
vendored
Normal file
541
vendor/k8s.io/kubernetes/pkg/kubelet/config/config.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
418
vendor/k8s.io/kubernetes/pkg/kubelet/config/config_test.go
generated
vendored
Normal file
418
vendor/k8s.io/kubernetes/pkg/kubelet/config/config_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
TestSource = "test"
|
||||
)
|
||||
|
||||
func expectEmptyChannel(t *testing.T, ch <-chan interface{}) {
|
||||
select {
|
||||
case update := <-ch:
|
||||
t.Errorf("Expected no update in channel, Got %v", update)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type sortedPods []*v1.Pod
|
||||
|
||||
func (s sortedPods) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s sortedPods) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s sortedPods) Less(i, j int) bool {
|
||||
return s[i].Namespace < s[j].Namespace
|
||||
}
|
||||
|
||||
func CreateValidPod(name, namespace string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
UID: types.UID(name), // for the purpose of testing, this is unique enough
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "ctr",
|
||||
Image: "image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePodUpdate(op kubetypes.PodOperation, source string, pods ...*v1.Pod) kubetypes.PodUpdate {
|
||||
return kubetypes.PodUpdate{Pods: pods, Op: op, Source: source}
|
||||
}
|
||||
|
||||
func createPodConfigTester(mode PodConfigNotificationMode) (chan<- interface{}, <-chan kubetypes.PodUpdate, *PodConfig) {
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
config := NewPodConfig(mode, eventBroadcaster.NewRecorder(v1.EventSource{Component: "kubelet"}))
|
||||
channel := config.Channel(TestSource)
|
||||
ch := config.Updates()
|
||||
return channel, ch, config
|
||||
}
|
||||
|
||||
func expectPodUpdate(t *testing.T, ch <-chan kubetypes.PodUpdate, expected ...kubetypes.PodUpdate) {
|
||||
for i := range expected {
|
||||
update := <-ch
|
||||
sort.Sort(sortedPods(update.Pods))
|
||||
sort.Sort(sortedPods(expected[i].Pods))
|
||||
// Make copies of the expected/actual update to compare all fields
|
||||
// except for "Pods", which are compared separately below.
|
||||
expectedCopy, updateCopy := expected[i], update
|
||||
expectedCopy.Pods, updateCopy.Pods = nil, nil
|
||||
if !api.Semantic.DeepEqual(expectedCopy, updateCopy) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expectedCopy, updateCopy)
|
||||
}
|
||||
|
||||
if len(expected[i].Pods) != len(update.Pods) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected[i], update)
|
||||
}
|
||||
// Compare pods one by one. This is necessary because we don't want to
|
||||
// compare local annotations.
|
||||
for j := range expected[i].Pods {
|
||||
if podsDifferSemantically(expected[i].Pods[j], update.Pods[j]) || !reflect.DeepEqual(expected[i].Pods[j].Status, update.Pods[j].Status) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected[i].Pods[j], update.Pods[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
expectNoPodUpdate(t, ch)
|
||||
}
|
||||
|
||||
func expectNoPodUpdate(t *testing.T, ch <-chan kubetypes.PodUpdate) {
|
||||
select {
|
||||
case update := <-ch:
|
||||
t.Errorf("Expected no update in channel, Got %#v", update)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPodAdded(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// see an update
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
|
||||
}
|
||||
|
||||
func TestNewPodAddedInvalidNamespace(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// see an update
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", ""))
|
||||
channel <- podUpdate
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource))
|
||||
}
|
||||
|
||||
func TestNewPodAddedDefaultNamespace(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// see an update
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default")))
|
||||
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "default")))
|
||||
}
|
||||
|
||||
func TestNewPodAddedDifferentNamespaces(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// see an update
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default")))
|
||||
|
||||
// see an update in another namespace
|
||||
podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "default"), CreateValidPod("foo", "new")))
|
||||
}
|
||||
|
||||
func TestInvalidPodFiltered(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// see an update
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
// add an invalid update
|
||||
podUpdate = CreatePodUpdate(kubetypes.UPDATE, TestSource, &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "foo"}})
|
||||
channel <- podUpdate
|
||||
expectNoPodUpdate(t, ch)
|
||||
}
|
||||
|
||||
func TestNewPodAddedSnapshotAndUpdates(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshotAndUpdates)
|
||||
|
||||
// see an set
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
|
||||
|
||||
// container updates are separated as UPDATE
|
||||
pod := *podUpdate.Pods[0]
|
||||
pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent}}
|
||||
channel <- CreatePodUpdate(kubetypes.ADD, TestSource, &pod)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, &pod))
|
||||
}
|
||||
|
||||
func TestNewPodAddedSnapshot(t *testing.T) {
|
||||
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshot)
|
||||
|
||||
// see an set
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
config.Sync()
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
|
||||
|
||||
// container updates are separated as UPDATE
|
||||
pod := *podUpdate.Pods[0]
|
||||
pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent}}
|
||||
channel <- CreatePodUpdate(kubetypes.ADD, TestSource, &pod)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, &pod))
|
||||
}
|
||||
|
||||
func TestNewPodAddedUpdatedRemoved(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// should register an add
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
|
||||
|
||||
// should ignore ADDs that are identical
|
||||
expectNoPodUpdate(t, ch)
|
||||
|
||||
// an kubetypes.ADD should be converted to kubetypes.UPDATE
|
||||
pod := CreateValidPod("foo", "new")
|
||||
pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent}}
|
||||
podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, pod)
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
|
||||
podUpdate = CreatePodUpdate(kubetypes.REMOVE, TestSource, &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "foo", Namespace: "new"}})
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.REMOVE, TestSource, pod))
|
||||
}
|
||||
|
||||
func TestNewPodAddedDelete(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// should register an add
|
||||
addedPod := CreateValidPod("foo", "new")
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, addedPod)
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, addedPod))
|
||||
|
||||
// mark this pod as deleted
|
||||
timestamp := metav1.NewTime(time.Now())
|
||||
deletedPod := CreateValidPod("foo", "new")
|
||||
deletedPod.ObjectMeta.DeletionTimestamp = ×tamp
|
||||
podUpdate = CreatePodUpdate(kubetypes.DELETE, TestSource, deletedPod)
|
||||
channel <- podUpdate
|
||||
// the existing pod should be gracefully deleted
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.DELETE, TestSource, addedPod))
|
||||
}
|
||||
|
||||
func TestNewPodAddedUpdatedSet(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// should register an add
|
||||
podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"), CreateValidPod("foo2", "new"), CreateValidPod("foo3", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"), CreateValidPod("foo2", "new"), CreateValidPod("foo3", "new")))
|
||||
|
||||
// should ignore ADDs that are identical
|
||||
expectNoPodUpdate(t, ch)
|
||||
|
||||
// should be converted to an kubetypes.ADD, kubetypes.REMOVE, and kubetypes.UPDATE
|
||||
pod := CreateValidPod("foo2", "new")
|
||||
pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent}}
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, pod, CreateValidPod("foo3", "new"), CreateValidPod("foo4", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch,
|
||||
CreatePodUpdate(kubetypes.REMOVE, TestSource, CreateValidPod("foo", "new")),
|
||||
CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo4", "new")),
|
||||
CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
}
|
||||
|
||||
func TestNewPodAddedSetReconciled(t *testing.T) {
|
||||
// Create and touch new test pods, return the new pods and touched pod. We should create new pod list
|
||||
// before touching to avoid data race.
|
||||
newTestPods := func(touchStatus, touchSpec bool) ([]*v1.Pod, *v1.Pod) {
|
||||
pods := []*v1.Pod{
|
||||
CreateValidPod("changeable-pod-0", "new"),
|
||||
CreateValidPod("constant-pod-1", "new"),
|
||||
CreateValidPod("constant-pod-2", "new"),
|
||||
}
|
||||
if touchStatus {
|
||||
pods[0].Status = v1.PodStatus{Message: strconv.Itoa(rand.Int())}
|
||||
}
|
||||
if touchSpec {
|
||||
pods[0].Spec.Containers[0].Name = strconv.Itoa(rand.Int())
|
||||
}
|
||||
return pods, pods[0]
|
||||
}
|
||||
for _, op := range []kubetypes.PodOperation{
|
||||
kubetypes.ADD,
|
||||
kubetypes.SET,
|
||||
} {
|
||||
var podWithStatusChange *v1.Pod
|
||||
pods, _ := newTestPods(false, false)
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
// Use SET to initialize the config, especially initialize the source set
|
||||
channel <- CreatePodUpdate(kubetypes.SET, TestSource, pods...)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, pods...))
|
||||
|
||||
// If status is not changed, no reconcile should be triggered
|
||||
channel <- CreatePodUpdate(op, TestSource, pods...)
|
||||
expectNoPodUpdate(t, ch)
|
||||
|
||||
// If the pod status is changed and not updated, a reconcile should be triggered
|
||||
pods, podWithStatusChange = newTestPods(true, false)
|
||||
channel <- CreatePodUpdate(op, TestSource, pods...)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.RECONCILE, TestSource, podWithStatusChange))
|
||||
|
||||
// If the pod status is changed, but the pod is also updated, no reconcile should be triggered
|
||||
pods, podWithStatusChange = newTestPods(true, true)
|
||||
channel <- CreatePodUpdate(op, TestSource, pods...)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, podWithStatusChange))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitialEmptySet(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
mode PodConfigNotificationMode
|
||||
op kubetypes.PodOperation
|
||||
}{
|
||||
{PodConfigNotificationIncremental, kubetypes.ADD},
|
||||
{PodConfigNotificationSnapshot, kubetypes.SET},
|
||||
{PodConfigNotificationSnapshotAndUpdates, kubetypes.SET},
|
||||
} {
|
||||
channel, ch, _ := createPodConfigTester(test.mode)
|
||||
|
||||
// should register an empty PodUpdate operation
|
||||
podUpdate := CreatePodUpdate(kubetypes.SET, TestSource)
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(test.op, TestSource))
|
||||
|
||||
// should ignore following empty sets
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource)
|
||||
channel <- podUpdate
|
||||
podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(test.op, TestSource, CreateValidPod("foo", "new")))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodUpdateAnnotations(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
pod := CreateValidPod("foo2", "new")
|
||||
pod.Annotations = make(map[string]string, 0)
|
||||
pod.Annotations["kubernetes.io/blah"] = "blah"
|
||||
|
||||
clone, err := conversion.NewCloner().DeepCopy(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
podUpdate := CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), clone.(*v1.Pod), CreateValidPod("foo3", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new")))
|
||||
|
||||
pod.Annotations["kubenetes.io/blah"] = "superblah"
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
|
||||
pod.Annotations["kubernetes.io/otherblah"] = "doh"
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
|
||||
delete(pod.Annotations, "kubernetes.io/blah")
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
}
|
||||
|
||||
func TestPodUpdateLabels(t *testing.T) {
|
||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||
|
||||
pod := CreateValidPod("foo2", "new")
|
||||
pod.Labels = make(map[string]string, 0)
|
||||
pod.Labels["key"] = "value"
|
||||
|
||||
clone, err := conversion.NewCloner().DeepCopy(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
podUpdate := CreatePodUpdate(kubetypes.SET, TestSource, clone.(*v1.Pod))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, pod))
|
||||
|
||||
pod.Labels["key"] = "newValue"
|
||||
podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, pod)
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
|
||||
|
||||
}
|
||||
18
vendor/k8s.io/kubernetes/pkg/kubelet/config/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/kubelet/config/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 config implements the pod configuration readers.
|
||||
package config // import "k8s.io/kubernetes/pkg/kubelet/config"
|
||||
202
vendor/k8s.io/kubernetes/pkg/kubelet/config/file.go
generated
vendored
Normal file
202
vendor/k8s.io/kubernetes/pkg/kubelet/config/file.go
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Reads the pod configuration from file or a directory of files.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
type sourceFile struct {
|
||||
path string
|
||||
nodeName types.NodeName
|
||||
store cache.Store
|
||||
fileKeyMapping map[string]string
|
||||
updates chan<- interface{}
|
||||
}
|
||||
|
||||
func NewSourceFile(path string, nodeName types.NodeName, period time.Duration, updates chan<- interface{}) {
|
||||
config := new(path, nodeName, period, updates)
|
||||
glog.V(1).Infof("Watching path %q", path)
|
||||
go wait.Forever(config.run, period)
|
||||
}
|
||||
|
||||
func new(path string, nodeName types.NodeName, period time.Duration, updates chan<- interface{}) *sourceFile {
|
||||
send := func(objs []interface{}) {
|
||||
var pods []*v1.Pod
|
||||
for _, o := range objs {
|
||||
pods = append(pods, o.(*v1.Pod))
|
||||
}
|
||||
updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source: kubetypes.FileSource}
|
||||
}
|
||||
store := cache.NewUndeltaStore(send, cache.MetaNamespaceKeyFunc)
|
||||
return &sourceFile{
|
||||
path: path,
|
||||
nodeName: nodeName,
|
||||
store: store,
|
||||
fileKeyMapping: map[string]string{},
|
||||
updates: updates,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sourceFile) run() {
|
||||
if err := s.watch(); err != nil {
|
||||
glog.Errorf("unable to read config path %q: %v", s.path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sourceFile) applyDefaults(pod *api.Pod, source string) error {
|
||||
return applyDefaults(pod, source, true, s.nodeName)
|
||||
}
|
||||
|
||||
func (s *sourceFile) resetStoreFromPath() error {
|
||||
path := s.path
|
||||
statInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
// Emit an update with an empty PodList to allow FileSource to be marked as seen
|
||||
s.updates <- kubetypes.PodUpdate{Pods: []*v1.Pod{}, Op: kubetypes.SET, Source: kubetypes.FileSource}
|
||||
return fmt.Errorf("path does not exist, ignoring")
|
||||
}
|
||||
|
||||
switch {
|
||||
case statInfo.Mode().IsDir():
|
||||
pods, err := s.extractFromDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pods) == 0 {
|
||||
// Emit an update with an empty PodList to allow FileSource to be marked as seen
|
||||
s.updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source: kubetypes.FileSource}
|
||||
return nil
|
||||
}
|
||||
return s.replaceStore(pods...)
|
||||
|
||||
case statInfo.Mode().IsRegular():
|
||||
pod, err := s.extractFromFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.replaceStore(pod)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("path is not a directory or file")
|
||||
}
|
||||
}
|
||||
|
||||
// Get as many pod configs as we can from a directory. Return an error if and only if something
|
||||
// prevented us from reading anything at all. Do not return an error if only some files
|
||||
// were problematic.
|
||||
func (s *sourceFile) extractFromDir(name string) ([]*v1.Pod, error) {
|
||||
dirents, err := filepath.Glob(filepath.Join(name, "[^.]*"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("glob failed: %v", err)
|
||||
}
|
||||
|
||||
pods := make([]*v1.Pod, 0)
|
||||
if len(dirents) == 0 {
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
sort.Strings(dirents)
|
||||
for _, path := range dirents {
|
||||
statInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("Can't get metadata for %q: %v", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case statInfo.Mode().IsDir():
|
||||
glog.V(1).Infof("Not recursing into config path %q", path)
|
||||
case statInfo.Mode().IsRegular():
|
||||
pod, err := s.extractFromFile(path)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("Can't process config file %q: %v", path, err)
|
||||
} else {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
default:
|
||||
glog.V(1).Infof("Config path %q is not a directory or file: %v", path, statInfo.Mode())
|
||||
}
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
func (s *sourceFile) extractFromFile(filename string) (pod *v1.Pod, err error) {
|
||||
glog.V(3).Infof("Reading config file %q", filename)
|
||||
defer func() {
|
||||
if err == nil && pod != nil {
|
||||
objKey, keyErr := cache.MetaNamespaceKeyFunc(pod)
|
||||
if keyErr != nil {
|
||||
err = keyErr
|
||||
return
|
||||
}
|
||||
s.fileKeyMapping[filename] = objKey
|
||||
}
|
||||
}()
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return pod, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return pod, err
|
||||
}
|
||||
|
||||
defaultFn := func(pod *api.Pod) error {
|
||||
return s.applyDefaults(pod, filename)
|
||||
}
|
||||
|
||||
parsed, pod, podErr := tryDecodeSinglePod(data, defaultFn)
|
||||
if parsed {
|
||||
if podErr != nil {
|
||||
return pod, podErr
|
||||
}
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
return pod, fmt.Errorf("%v: read '%v', but couldn't parse as pod(%v).\n",
|
||||
filename, string(data), podErr)
|
||||
}
|
||||
|
||||
func (s *sourceFile) replaceStore(pods ...*v1.Pod) (err error) {
|
||||
objs := []interface{}{}
|
||||
for _, pod := range pods {
|
||||
objs = append(objs, pod)
|
||||
}
|
||||
return s.store.Replace(objs, "")
|
||||
}
|
||||
124
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_linux.go
generated
vendored
Normal file
124
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Reads the pod configuration from file or a directory of files.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/exp/inotify"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
type podEventType int
|
||||
|
||||
const (
|
||||
podAdd podEventType = iota
|
||||
podModify
|
||||
podDelete
|
||||
)
|
||||
|
||||
func (s *sourceFile) watch() error {
|
||||
_, err := os.Stat(s.path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
// Emit an update with an empty PodList to allow FileSource to be marked as seen
|
||||
s.updates <- kubetypes.PodUpdate{Pods: []*v1.Pod{}, Op: kubetypes.SET, Source: kubetypes.FileSource}
|
||||
return fmt.Errorf("path does not exist, ignoring")
|
||||
}
|
||||
|
||||
w, err := inotify.NewWatcher()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create inotify: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
err = w.AddWatch(s.path, inotify.IN_DELETE_SELF|inotify.IN_CREATE|inotify.IN_MOVED_TO|inotify.IN_MODIFY|inotify.IN_MOVED_FROM|inotify.IN_DELETE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create inotify for path %q: %v", s.path, err)
|
||||
}
|
||||
|
||||
// Reset store with config files already existing when starting
|
||||
if err := s.resetStoreFromPath(); err != nil {
|
||||
return fmt.Errorf("unable to read config path %q: %v", s.path, err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case event := <-w.Event:
|
||||
err = s.processEvent(event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while processing event (%+v): %v", event, err)
|
||||
}
|
||||
case err = <-w.Error:
|
||||
return fmt.Errorf("error while watching %q: %v", s.path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sourceFile) processEvent(e *inotify.Event) error {
|
||||
var eventType podEventType
|
||||
switch {
|
||||
case (e.Mask & inotify.IN_ISDIR) > 0:
|
||||
glog.V(1).Infof("Not recursing into config path %q", s.path)
|
||||
return nil
|
||||
case (e.Mask & inotify.IN_CREATE) > 0:
|
||||
eventType = podAdd
|
||||
case (e.Mask & inotify.IN_MOVED_TO) > 0:
|
||||
eventType = podAdd
|
||||
case (e.Mask & inotify.IN_MODIFY) > 0:
|
||||
eventType = podModify
|
||||
case (e.Mask & inotify.IN_DELETE) > 0:
|
||||
eventType = podDelete
|
||||
case (e.Mask & inotify.IN_MOVED_FROM) > 0:
|
||||
eventType = podDelete
|
||||
case (e.Mask & inotify.IN_DELETE_SELF) > 0:
|
||||
return fmt.Errorf("the watched path is deleted")
|
||||
default:
|
||||
// Ignore rest events
|
||||
return nil
|
||||
}
|
||||
|
||||
switch eventType {
|
||||
case podAdd, podModify:
|
||||
if pod, err := s.extractFromFile(e.Name); err != nil {
|
||||
glog.Errorf("can't process config file %q: %v", e.Name, err)
|
||||
} else {
|
||||
return s.store.Add(pod)
|
||||
}
|
||||
case podDelete:
|
||||
if objKey, keyExist := s.fileKeyMapping[e.Name]; keyExist {
|
||||
pod, podExist, err := s.store.GetByKey(objKey)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !podExist {
|
||||
return fmt.Errorf("the pod with key %s doesn't exist in cache", objKey)
|
||||
} else {
|
||||
return s.store.Delete(pod)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
421
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_linux_test.go
generated
vendored
Normal file
421
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_linux_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utiltesting "k8s.io/kubernetes/pkg/util/testing"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestExtractFromNonExistentFile(t *testing.T) {
|
||||
ch := make(chan interface{}, 1)
|
||||
c := new("/some/fake/file", "localhost", time.Millisecond, ch)
|
||||
err := c.watch()
|
||||
if err == nil {
|
||||
t.Errorf("Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOnNonExistentFile(t *testing.T) {
|
||||
ch := make(chan interface{})
|
||||
NewSourceFile("random_non_existent_path", "localhost", time.Millisecond, ch)
|
||||
select {
|
||||
case got := <-ch:
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
expected := CreatePodUpdate(kubetypes.SET, kubetypes.FileSource)
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("expected update, timeout instead")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPodsFromFileExistAlready(t *testing.T) {
|
||||
hostname := types.NodeName("random-test-hostname")
|
||||
var testCases = getTestCases(hostname)
|
||||
|
||||
for _, testCase := range testCases {
|
||||
func() {
|
||||
dirName, err := utiltesting.MkTmpdir("file-test")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
file := testCase.writeToFile(dirName, "test_pod_config", t)
|
||||
|
||||
ch := make(chan interface{})
|
||||
NewSourceFile(file, hostname, time.Millisecond, ch)
|
||||
select {
|
||||
case got := <-ch:
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
for _, pod := range update.Pods {
|
||||
// TODO: remove the conversion when validation is performed on versioned objects.
|
||||
internalPod := &api.Pod{}
|
||||
if err := v1.Convert_v1_Pod_To_api_Pod(pod, internalPod, nil); err != nil {
|
||||
t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
|
||||
}
|
||||
if errs := validation.ValidatePod(internalPod); len(errs) > 0 {
|
||||
t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
|
||||
}
|
||||
}
|
||||
if !api.Semantic.DeepEqual(testCase.expected, update) {
|
||||
t.Fatalf("%s: Expected %#v, Got %#v", testCase.desc, testCase.expected, update)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("%s: Expected update, timeout instead", testCase.desc)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPodsFromFileExistLater(t *testing.T) {
|
||||
watchFileAdded(false, t)
|
||||
}
|
||||
|
||||
func TestReadPodsFromFileChanged(t *testing.T) {
|
||||
watchFileChanged(false, t)
|
||||
}
|
||||
|
||||
func TestReadPodsFromFileInDirAdded(t *testing.T) {
|
||||
watchFileAdded(true, t)
|
||||
}
|
||||
|
||||
func TestReadPodsFromFileInDirChanged(t *testing.T) {
|
||||
watchFileChanged(true, t)
|
||||
}
|
||||
|
||||
func TestExtractFromBadDataFile(t *testing.T) {
|
||||
dirName, err := utiltesting.MkTmpdir("file-test")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
fileName := filepath.Join(dirName, "test_pod_config")
|
||||
err = ioutil.WriteFile(fileName, []byte{1, 2, 3}, 0555)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to write test file %#v", err)
|
||||
}
|
||||
|
||||
ch := make(chan interface{}, 1)
|
||||
c := new(fileName, "localhost", time.Millisecond, ch)
|
||||
err = c.resetStoreFromPath()
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got nil")
|
||||
}
|
||||
expectEmptyChannel(t, ch)
|
||||
}
|
||||
|
||||
func TestExtractFromEmptyDir(t *testing.T) {
|
||||
dirName, err := utiltesting.MkTmpdir("file-test")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
ch := make(chan interface{}, 1)
|
||||
c := new(dirName, "localhost", time.Millisecond, ch)
|
||||
err = c.resetStoreFromPath()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
update := (<-ch).(kubetypes.PodUpdate)
|
||||
expected := CreatePodUpdate(kubetypes.SET, kubetypes.FileSource)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
t.Fatalf("expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
desc string
|
||||
pod runtime.Object
|
||||
expected kubetypes.PodUpdate
|
||||
}
|
||||
|
||||
func getTestCases(hostname types.NodeName) []*testCase {
|
||||
grace := int64(30)
|
||||
return []*testCase{
|
||||
{
|
||||
desc: "Simple pod",
|
||||
pod: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "test",
|
||||
UID: "12345",
|
||||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
expected: CreatePodUpdate(kubetypes.SET, kubetypes.FileSource, &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "test-" + string(hostname),
|
||||
UID: "12345",
|
||||
Namespace: "mynamespace",
|
||||
Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "12345"},
|
||||
SelfLink: getSelfLink("test-"+string(hostname), "mynamespace"),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: string(hostname),
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
Containers: []v1.Container{{
|
||||
Name: "image",
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *testCase) writeToFile(dir, name string, t *testing.T) string {
|
||||
var versionedPod runtime.Object
|
||||
err := testapi.Default.Converter().Convert(&tc.pod, &versionedPod, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error in versioning the pod: %v", tc.desc, err)
|
||||
}
|
||||
fileContents, err := runtime.Encode(testapi.Default.Codec(), versionedPod)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error in encoding the pod: %v", tc.desc, err)
|
||||
}
|
||||
|
||||
fileName := filepath.Join(dir, name)
|
||||
if err := writeFile(fileName, []byte(fileContents)); err != nil {
|
||||
t.Fatalf("unable to write test file %#v", err)
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
func watchFileAdded(watchDir bool, t *testing.T) {
|
||||
hostname := types.NodeName("random-test-hostname")
|
||||
var testCases = getTestCases(hostname)
|
||||
|
||||
fileNamePre := "test_pod_config"
|
||||
for index, testCase := range testCases {
|
||||
func() {
|
||||
dirName, err := utiltesting.MkTmpdir("dir-test")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
fileName := fmt.Sprintf("%s_%d", fileNamePre, index)
|
||||
|
||||
ch := make(chan interface{})
|
||||
if watchDir {
|
||||
NewSourceFile(dirName, hostname, 100*time.Millisecond, ch)
|
||||
} else {
|
||||
NewSourceFile(filepath.Join(dirName, fileName), hostname, 100*time.Millisecond, ch)
|
||||
}
|
||||
expectEmptyUpdate(t, ch)
|
||||
|
||||
addFile := func() {
|
||||
// Add a file
|
||||
testCase.writeToFile(dirName, fileName, t)
|
||||
}
|
||||
|
||||
go addFile()
|
||||
|
||||
// For !watchDir: expect an update by SourceFile.resetStoreFromPath().
|
||||
// For watchDir: expect at least one update from CREATE & MODIFY inotify event.
|
||||
// Shouldn't expect two updates from CREATE & MODIFY because CREATE doesn't guarantee file written.
|
||||
// In that case no update will be sent from CREATE event.
|
||||
expectUpdate(t, ch, testCase)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func watchFileChanged(watchDir bool, t *testing.T) {
|
||||
hostname := types.NodeName("random-test-hostname")
|
||||
var testCases = getTestCases(hostname)
|
||||
|
||||
fileNamePre := "test_pod_config"
|
||||
for index, testCase := range testCases {
|
||||
func() {
|
||||
dirName, err := utiltesting.MkTmpdir("dir-test")
|
||||
fileName := fmt.Sprintf("%s_%d", fileNamePre, index)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dirName)
|
||||
|
||||
var file string
|
||||
lock := &sync.Mutex{}
|
||||
ch := make(chan interface{})
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
file = testCase.writeToFile(dirName, fileName, t)
|
||||
}()
|
||||
|
||||
if watchDir {
|
||||
NewSourceFile(dirName, hostname, 100*time.Millisecond, ch)
|
||||
defer func() {
|
||||
// Remove the file
|
||||
deleteFile(dirName, fileName, ch, t)
|
||||
}()
|
||||
} else {
|
||||
NewSourceFile(file, hostname, 100*time.Millisecond, ch)
|
||||
}
|
||||
// expect an update by SourceFile.resetStoreFromPath()
|
||||
expectUpdate(t, ch, testCase)
|
||||
|
||||
changeFile := func() {
|
||||
// Edit the file content
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
pod := testCase.pod.(*v1.Pod)
|
||||
pod.Spec.Containers[0].Name = "image2"
|
||||
|
||||
testCase.expected.Pods[0].Spec.Containers[0].Name = "image2"
|
||||
testCase.writeToFile(dirName, fileName, t)
|
||||
}
|
||||
|
||||
go changeFile()
|
||||
// expect an update by MODIFY inotify event
|
||||
expectUpdate(t, ch, testCase)
|
||||
|
||||
if watchDir {
|
||||
from := fileName
|
||||
fileName = fileName + "_ch"
|
||||
go changeFileName(dirName, from, fileName, t)
|
||||
// expect an update by MOVED_FROM inotify event cause changing file name
|
||||
expectEmptyUpdate(t, ch)
|
||||
// expect an update by MOVED_TO inotify event cause changing file name
|
||||
expectUpdate(t, ch, testCase)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func deleteFile(dir, file string, ch chan interface{}, t *testing.T) {
|
||||
go func() {
|
||||
path := filepath.Join(dir, file)
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
t.Errorf("unable to remove test file %s: %s", path, err)
|
||||
}
|
||||
}()
|
||||
|
||||
expectEmptyUpdate(t, ch)
|
||||
}
|
||||
|
||||
func expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) {
|
||||
timer := time.After(5 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case got := <-ch:
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
for _, pod := range update.Pods {
|
||||
// TODO: remove the conversion when validation is performed on versioned objects.
|
||||
internalPod := &api.Pod{}
|
||||
if err := v1.Convert_v1_Pod_To_api_Pod(pod, internalPod, nil); err != nil {
|
||||
t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
|
||||
}
|
||||
if errs := validation.ValidatePod(internalPod); len(errs) > 0 {
|
||||
t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
|
||||
}
|
||||
}
|
||||
|
||||
if !api.Semantic.DeepEqual(testCase.expected, update) {
|
||||
t.Fatalf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update)
|
||||
}
|
||||
return
|
||||
case <-timer:
|
||||
t.Fatalf("%s: Expected update, timeout instead", testCase.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectEmptyUpdate(t *testing.T, ch chan interface{}) {
|
||||
timer := time.After(5 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case got := <-ch:
|
||||
update := got.(kubetypes.PodUpdate)
|
||||
if len(update.Pods) != 0 {
|
||||
t.Fatalf("expected empty update, got %#v", update)
|
||||
}
|
||||
return
|
||||
case <-timer:
|
||||
t.Fatalf("expected empty update, timeout instead")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFile(filename string, data []byte) error {
|
||||
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := f.Write(data)
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func changeFileName(dir, from, to string, t *testing.T) {
|
||||
fromPath := filepath.Join(dir, from)
|
||||
toPath := filepath.Join(dir, to)
|
||||
if err := exec.Command("mv", fromPath, toPath).Run(); err != nil {
|
||||
t.Errorf("Fail to change file name: %s", err)
|
||||
}
|
||||
}
|
||||
26
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_unsupported.go
generated
vendored
Normal file
26
vendor/k8s.io/kubernetes/pkg/kubelet/config/file_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Reads the pod configuration from file or a directory of files.
|
||||
package config
|
||||
|
||||
import "errors"
|
||||
|
||||
func (s *sourceFile) watch() error {
|
||||
return errors.New("source file is unsupported in this build")
|
||||
}
|
||||
143
vendor/k8s.io/kubernetes/pkg/kubelet/config/http.go
generated
vendored
Normal file
143
vendor/k8s.io/kubernetes/pkg/kubelet/config/http.go
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Reads the pod configuration from an HTTP GET response.
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type sourceURL struct {
|
||||
url string
|
||||
header http.Header
|
||||
nodeName types.NodeName
|
||||
updates chan<- interface{}
|
||||
data []byte
|
||||
failureLogs int
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewSourceURL(url string, header http.Header, nodeName types.NodeName, period time.Duration, updates chan<- interface{}) {
|
||||
config := &sourceURL{
|
||||
url: url,
|
||||
header: header,
|
||||
nodeName: nodeName,
|
||||
updates: updates,
|
||||
data: nil,
|
||||
// Timing out requests leads to retries. This client is only used to
|
||||
// read the manifest URL passed to kubelet.
|
||||
client: &http.Client{Timeout: 10 * time.Second},
|
||||
}
|
||||
glog.V(1).Infof("Watching URL %s", url)
|
||||
go wait.Until(config.run, period, wait.NeverStop)
|
||||
}
|
||||
|
||||
func (s *sourceURL) run() {
|
||||
if err := s.extractFromURL(); err != nil {
|
||||
// Don't log this multiple times per minute. The first few entries should be
|
||||
// enough to get the point across.
|
||||
if s.failureLogs < 3 {
|
||||
glog.Warningf("Failed to read pods from URL: %v", err)
|
||||
} else if s.failureLogs == 3 {
|
||||
glog.Warningf("Failed to read pods from URL. Dropping verbosity of this message to V(4): %v", err)
|
||||
} else {
|
||||
glog.V(4).Infof("Failed to read pods from URL: %v", err)
|
||||
}
|
||||
s.failureLogs++
|
||||
} else {
|
||||
if s.failureLogs > 0 {
|
||||
glog.Info("Successfully read pods from URL.")
|
||||
s.failureLogs = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sourceURL) applyDefaults(pod *api.Pod) error {
|
||||
return applyDefaults(pod, s.url, false, s.nodeName)
|
||||
}
|
||||
|
||||
func (s *sourceURL) extractFromURL() error {
|
||||
req, err := http.NewRequest("GET", s.url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header = s.header
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("%v: %v", s.url, resp.Status)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
// Emit an update with an empty PodList to allow HTTPSource to be marked as seen
|
||||
s.updates <- kubetypes.PodUpdate{Pods: []*v1.Pod{}, Op: kubetypes.SET, Source: kubetypes.HTTPSource}
|
||||
return fmt.Errorf("zero-length data received from %v", s.url)
|
||||
}
|
||||
// Short circuit if the data has not changed since the last time it was read.
|
||||
if bytes.Compare(data, s.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
s.data = data
|
||||
|
||||
// First try as it is a single pod.
|
||||
parsed, pod, singlePodErr := tryDecodeSinglePod(data, s.applyDefaults)
|
||||
if parsed {
|
||||
if singlePodErr != nil {
|
||||
// It parsed but could not be used.
|
||||
return singlePodErr
|
||||
}
|
||||
s.updates <- kubetypes.PodUpdate{Pods: []*v1.Pod{pod}, Op: kubetypes.SET, Source: kubetypes.HTTPSource}
|
||||
return nil
|
||||
}
|
||||
|
||||
// That didn't work, so try a list of pods.
|
||||
parsed, podList, multiPodErr := tryDecodePodList(data, s.applyDefaults)
|
||||
if parsed {
|
||||
if multiPodErr != nil {
|
||||
// It parsed but could not be used.
|
||||
return multiPodErr
|
||||
}
|
||||
pods := make([]*v1.Pod, 0)
|
||||
for i := range podList.Items {
|
||||
pods = append(pods, &podList.Items[i])
|
||||
}
|
||||
s.updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source: kubetypes.HTTPSource}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%v: received '%v', but couldn't parse as "+
|
||||
"single (%v) or multiple pods (%v).\n",
|
||||
s.url, string(data), singlePodErr, multiPodErr)
|
||||
}
|
||||
362
vendor/k8s.io/kubernetes/pkg/kubelet/config/http_test.go
generated
vendored
Normal file
362
vendor/k8s.io/kubernetes/pkg/kubelet/config/http_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utiltesting "k8s.io/kubernetes/pkg/util/testing"
|
||||
)
|
||||
|
||||
func TestURLErrorNotExistNoUpdate(t *testing.T) {
|
||||
ch := make(chan interface{})
|
||||
NewSourceURL("http://localhost:49575/_not_found_", http.Header{}, "localhost", time.Millisecond, ch)
|
||||
select {
|
||||
case got := <-ch:
|
||||
t.Errorf("Expected no update, Got %#v", got)
|
||||
case <-time.After(2 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractFromHttpBadness(t *testing.T) {
|
||||
ch := make(chan interface{}, 1)
|
||||
c := sourceURL{"http://localhost:49575/_not_found_", http.Header{}, "other", ch, nil, 0, http.DefaultClient}
|
||||
if err := c.extractFromURL(); err == nil {
|
||||
t.Errorf("Expected error")
|
||||
}
|
||||
expectEmptyChannel(t, ch)
|
||||
}
|
||||
|
||||
func TestExtractInvalidPods(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
desc string
|
||||
pod *v1.Pod
|
||||
}{
|
||||
{
|
||||
desc: "No version",
|
||||
pod: &v1.Pod{TypeMeta: metav1.TypeMeta{APIVersion: ""}},
|
||||
},
|
||||
{
|
||||
desc: "Invalid version",
|
||||
pod: &v1.Pod{TypeMeta: metav1.TypeMeta{APIVersion: "v1betta2"}},
|
||||
},
|
||||
{
|
||||
desc: "Invalid volume name",
|
||||
pod: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{{Name: "_INVALID_"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Duplicate volume names",
|
||||
pod: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{{Name: "repeated"}, {Name: "repeated"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Unspecified container name",
|
||||
pod: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{Name: ""}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Invalid container name",
|
||||
pod: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{Name: "_INVALID_"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
data, err := json.Marshal(testCase.pod)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Some weird json problem: %v", testCase.desc, err)
|
||||
}
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 200,
|
||||
ResponseBody: string(data),
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
ch := make(chan interface{}, 1)
|
||||
c := sourceURL{testServer.URL, http.Header{}, "localhost", ch, nil, 0, http.DefaultClient}
|
||||
if err := c.extractFromURL(); err == nil {
|
||||
t.Errorf("%s: Expected error", testCase.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractPodsFromHTTP(t *testing.T) {
|
||||
nodeName := "different-value"
|
||||
|
||||
grace := int64(30)
|
||||
var testCases = []struct {
|
||||
desc string
|
||||
pods runtime.Object
|
||||
expected kubetypes.PodUpdate
|
||||
}{
|
||||
{
|
||||
desc: "Single pod",
|
||||
pods: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
UID: "111",
|
||||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: string(nodeName),
|
||||
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
expected: CreatePodUpdate(kubetypes.SET,
|
||||
kubetypes.HTTPSource,
|
||||
&v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
UID: "111",
|
||||
Name: "foo" + "-" + nodeName,
|
||||
Namespace: "mynamespace",
|
||||
Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"},
|
||||
SelfLink: getSelfLink("foo-"+nodeName, "mynamespace"),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
|
||||
Containers: []v1.Container{{
|
||||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
}},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
desc: "Multiple pods",
|
||||
pods: &v1.PodList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PodList",
|
||||
APIVersion: "",
|
||||
},
|
||||
Items: []v1.Pod{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
UID: "111",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "bar",
|
||||
UID: "222",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: ""}},
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: CreatePodUpdate(kubetypes.SET,
|
||||
kubetypes.HTTPSource,
|
||||
&v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
UID: "111",
|
||||
Name: "foo" + "-" + nodeName,
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"},
|
||||
SelfLink: getSelfLink("foo-"+nodeName, kubetypes.NamespaceDefault),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
|
||||
Containers: []v1.Container{{
|
||||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
}},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
&v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
UID: "222",
|
||||
Name: "bar" + "-" + nodeName,
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "222"},
|
||||
SelfLink: getSelfLink("bar-"+nodeName, kubetypes.NamespaceDefault),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
SecurityContext: &v1.PodSecurityContext{},
|
||||
|
||||
Containers: []v1.Container{{
|
||||
Name: "2",
|
||||
Image: "bar:bartag",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
}},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
var versionedPods runtime.Object
|
||||
err := testapi.Default.Converter().Convert(&testCase.pods, &versionedPods, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err)
|
||||
}
|
||||
data, err := runtime.Encode(testapi.Default.Codec(), versionedPods)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err)
|
||||
}
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 200,
|
||||
ResponseBody: string(data),
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
ch := make(chan interface{}, 1)
|
||||
c := sourceURL{testServer.URL, http.Header{}, types.NodeName(nodeName), ch, nil, 0, http.DefaultClient}
|
||||
if err := c.extractFromURL(); err != nil {
|
||||
t.Errorf("%s: Unexpected error: %v", testCase.desc, err)
|
||||
continue
|
||||
}
|
||||
update := (<-ch).(kubetypes.PodUpdate)
|
||||
|
||||
if !api.Semantic.DeepEqual(testCase.expected, update) {
|
||||
t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update)
|
||||
}
|
||||
for _, pod := range update.Pods {
|
||||
// TODO: remove the conversion when validation is performed on versioned objects.
|
||||
internalPod := &api.Pod{}
|
||||
if err := v1.Convert_v1_Pod_To_api_Pod(pod, internalPod, nil); err != nil {
|
||||
t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
|
||||
}
|
||||
if errs := validation.ValidatePod(internalPod); len(errs) != 0 {
|
||||
t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errs.ToAggregate())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLWithHeader(t *testing.T) {
|
||||
pod := &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
UID: "111",
|
||||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: "localhost",
|
||||
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected json marshalling error: %v", err)
|
||||
}
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 200,
|
||||
ResponseBody: string(data),
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
ch := make(chan interface{}, 1)
|
||||
header := make(http.Header)
|
||||
header.Set("Metadata-Flavor", "Google")
|
||||
c := sourceURL{testServer.URL, header, "localhost", ch, nil, 0, http.DefaultClient}
|
||||
if err := c.extractFromURL(); err != nil {
|
||||
t.Fatalf("Unexpected error extracting from URL: %v", err)
|
||||
}
|
||||
update := (<-ch).(kubetypes.PodUpdate)
|
||||
|
||||
headerVal := fakeHandler.RequestReceived.Header["Metadata-Flavor"]
|
||||
if len(headerVal) != 1 || headerVal[0] != "Google" {
|
||||
t.Errorf("Header missing expected entry %v. Got %v", header, fakeHandler.RequestReceived.Header)
|
||||
}
|
||||
if len(update.Pods) != 1 {
|
||||
t.Errorf("Received wrong number of pods, expected one: %v", update.Pods)
|
||||
}
|
||||
}
|
||||
67
vendor/k8s.io/kubernetes/pkg/kubelet/config/sources.go
generated
vendored
Normal file
67
vendor/k8s.io/kubernetes/pkg/kubelet/config/sources.go
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
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 config implements the pod configuration readers.
|
||||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
// SourcesReadyFn is function that returns true if the specified sources have been seen.
|
||||
type SourcesReadyFn func(sourcesSeen sets.String) bool
|
||||
|
||||
// SourcesReady tracks the set of configured sources seen by the kubelet.
|
||||
type SourcesReady interface {
|
||||
// AddSource adds the specified source to the set of sources managed.
|
||||
AddSource(source string)
|
||||
// AllReady returns true if the currently configured sources have all been seen.
|
||||
AllReady() bool
|
||||
}
|
||||
|
||||
// NewSourcesReady returns a SourcesReady with the specified function.
|
||||
func NewSourcesReady(sourcesReadyFn SourcesReadyFn) SourcesReady {
|
||||
return &sourcesImpl{
|
||||
sourcesSeen: sets.NewString(),
|
||||
sourcesReadyFn: sourcesReadyFn,
|
||||
}
|
||||
}
|
||||
|
||||
// sourcesImpl implements SourcesReady. It is thread-safe.
|
||||
type sourcesImpl struct {
|
||||
// lock protects access to sources seen.
|
||||
lock sync.RWMutex
|
||||
// set of sources seen.
|
||||
sourcesSeen sets.String
|
||||
// sourcesReady is a function that evaluates if the sources are ready.
|
||||
sourcesReadyFn SourcesReadyFn
|
||||
}
|
||||
|
||||
// Add adds the specified source to the set of sources managed.
|
||||
func (s *sourcesImpl) AddSource(source string) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.sourcesSeen.Insert(source)
|
||||
}
|
||||
|
||||
// AllReady returns true if each configured source is ready.
|
||||
func (s *sourcesImpl) AllReady() bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.sourcesReadyFn(s.sourcesSeen)
|
||||
}
|
||||
78
vendor/k8s.io/kubernetes/pkg/kubelet/container/BUILD
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/pkg/kubelet/container/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
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 = [
|
||||
"cache.go",
|
||||
"container_gc.go",
|
||||
"container_reference_manager.go",
|
||||
"helpers.go",
|
||||
"os.go",
|
||||
"pty_linux.go",
|
||||
"ref.go",
|
||||
"resize.go",
|
||||
"runtime.go",
|
||||
"runtime_cache.go",
|
||||
"runtime_cache_fake.go",
|
||||
"sync_result.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
|
||||
"//pkg/kubelet/util/format:go_default_library",
|
||||
"//pkg/kubelet/util/ioutils:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/hash:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//third_party/forked/golang/expansion:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/kr/pty",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cache_test.go",
|
||||
"helpers_test.go",
|
||||
"ref_test.go",
|
||||
"sync_result_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["runtime_cache_test.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
199
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go
generated
vendored
Normal file
199
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
// Cache stores the PodStatus for the pods. It represents *all* the visible
|
||||
// pods/containers in the container runtime. All cache entries are at least as
|
||||
// new or newer than the global timestamp (set by UpdateTime()), while
|
||||
// individual entries may be slightly newer than the global timestamp. If a pod
|
||||
// has no states known by the runtime, Cache returns an empty PodStatus object
|
||||
// with ID populated.
|
||||
//
|
||||
// Cache provides two methods to retrive the PodStatus: the non-blocking Get()
|
||||
// and the blocking GetNewerThan() method. The component responsible for
|
||||
// populating the cache is expected to call Delete() to explicitly free the
|
||||
// cache entries.
|
||||
type Cache interface {
|
||||
Get(types.UID) (*PodStatus, error)
|
||||
Set(types.UID, *PodStatus, error, time.Time)
|
||||
// GetNewerThan is a blocking call that only returns the status
|
||||
// when it is newer than the given time.
|
||||
GetNewerThan(types.UID, time.Time) (*PodStatus, error)
|
||||
Delete(types.UID)
|
||||
UpdateTime(time.Time)
|
||||
}
|
||||
|
||||
type data struct {
|
||||
// Status of the pod.
|
||||
status *PodStatus
|
||||
// Error got when trying to inspect the pod.
|
||||
err error
|
||||
// Time when the data was last modified.
|
||||
modified time.Time
|
||||
}
|
||||
|
||||
type subRecord struct {
|
||||
time time.Time
|
||||
ch chan *data
|
||||
}
|
||||
|
||||
// cache implements Cache.
|
||||
type cache struct {
|
||||
// Lock which guards all internal data structures.
|
||||
lock sync.RWMutex
|
||||
// Map that stores the pod statuses.
|
||||
pods map[types.UID]*data
|
||||
// A global timestamp represents how fresh the cached data is. All
|
||||
// cache content is at the least newer than this timestamp. Note that the
|
||||
// timestamp is nil after initialization, and will only become non-nil when
|
||||
// it is ready to serve the cached statuses.
|
||||
timestamp *time.Time
|
||||
// Map that stores the subscriber records.
|
||||
subscribers map[types.UID][]*subRecord
|
||||
}
|
||||
|
||||
// NewCache creates a pod cache.
|
||||
func NewCache() Cache {
|
||||
return &cache{pods: map[types.UID]*data{}, subscribers: map[types.UID][]*subRecord{}}
|
||||
}
|
||||
|
||||
// Get returns the PodStatus for the pod; callers are expected not to
|
||||
// modify the objects returned.
|
||||
func (c *cache) Get(id types.UID) (*PodStatus, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
d := c.get(id)
|
||||
return d.status, d.err
|
||||
}
|
||||
|
||||
func (c *cache) GetNewerThan(id types.UID, minTime time.Time) (*PodStatus, error) {
|
||||
ch := c.subscribe(id, minTime)
|
||||
d := <-ch
|
||||
return d.status, d.err
|
||||
}
|
||||
|
||||
// Set sets the PodStatus for the pod.
|
||||
func (c *cache) Set(id types.UID, status *PodStatus, err error, timestamp time.Time) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
defer c.notify(id, timestamp)
|
||||
c.pods[id] = &data{status: status, err: err, modified: timestamp}
|
||||
}
|
||||
|
||||
// Delete removes the entry of the pod.
|
||||
func (c *cache) Delete(id types.UID) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
delete(c.pods, id)
|
||||
}
|
||||
|
||||
// UpdateTime modifies the global timestamp of the cache and notify
|
||||
// subscribers if needed.
|
||||
func (c *cache) UpdateTime(timestamp time.Time) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.timestamp = ×tamp
|
||||
// Notify all the subscribers if the condition is met.
|
||||
for id := range c.subscribers {
|
||||
c.notify(id, *c.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
func makeDefaultData(id types.UID) *data {
|
||||
return &data{status: &PodStatus{ID: id}, err: nil}
|
||||
}
|
||||
|
||||
func (c *cache) get(id types.UID) *data {
|
||||
d, ok := c.pods[id]
|
||||
if !ok {
|
||||
// Cache should store *all* pod/container information known by the
|
||||
// container runtime. A cache miss indicates that there are no states
|
||||
// regarding the pod last time we queried the container runtime.
|
||||
// What this *really* means is that there are no visible pod/containers
|
||||
// associated with this pod. Simply return an default (mostly empty)
|
||||
// PodStatus to reflect this.
|
||||
return makeDefaultData(id)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// getIfNewerThan returns the data it is newer than the given time.
|
||||
// Otherwise, it returns nil. The caller should acquire the lock.
|
||||
func (c *cache) getIfNewerThan(id types.UID, minTime time.Time) *data {
|
||||
d, ok := c.pods[id]
|
||||
globalTimestampIsNewer := (c.timestamp != nil && c.timestamp.After(minTime))
|
||||
if !ok && globalTimestampIsNewer {
|
||||
// Status is not cached, but the global timestamp is newer than
|
||||
// minTime, return the default status.
|
||||
return makeDefaultData(id)
|
||||
}
|
||||
if ok && (d.modified.After(minTime) || globalTimestampIsNewer) {
|
||||
// Status is cached, return status if either of the following is true.
|
||||
// * status was modified after minTime
|
||||
// * the global timestamp of the cache is newer than minTime.
|
||||
return d
|
||||
}
|
||||
// The pod status is not ready.
|
||||
return nil
|
||||
}
|
||||
|
||||
// notify sends notifications for pod with the given id, if the requirements
|
||||
// are met. Note that the caller should acquire the lock.
|
||||
func (c *cache) notify(id types.UID, timestamp time.Time) {
|
||||
list, ok := c.subscribers[id]
|
||||
if !ok {
|
||||
// No one to notify.
|
||||
return
|
||||
}
|
||||
newList := []*subRecord{}
|
||||
for i, r := range list {
|
||||
if timestamp.Before(r.time) {
|
||||
// Doesn't meet the time requirement; keep the record.
|
||||
newList = append(newList, list[i])
|
||||
continue
|
||||
}
|
||||
r.ch <- c.get(id)
|
||||
close(r.ch)
|
||||
}
|
||||
if len(newList) == 0 {
|
||||
delete(c.subscribers, id)
|
||||
} else {
|
||||
c.subscribers[id] = newList
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cache) subscribe(id types.UID, timestamp time.Time) chan *data {
|
||||
ch := make(chan *data, 1)
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
d := c.getIfNewerThan(id, timestamp)
|
||||
if d != nil {
|
||||
// If the cache entry is ready, send the data and return immediately.
|
||||
ch <- d
|
||||
return ch
|
||||
}
|
||||
// Add the subscription record.
|
||||
c.subscribers[id] = append(c.subscribers[id], &subRecord{time: timestamp, ch: ch})
|
||||
return ch
|
||||
}
|
||||
210
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache_test.go
generated
vendored
Normal file
210
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
func newTestCache() *cache {
|
||||
c := NewCache()
|
||||
return c.(*cache)
|
||||
}
|
||||
|
||||
func TestCacheNotInitialized(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
// If the global timestamp is not set, always return nil.
|
||||
d := cache.getIfNewerThan(types.UID("1234"), time.Time{})
|
||||
assert.True(t, d == nil, "should return nil since cache is not initialized")
|
||||
}
|
||||
|
||||
func getTestPodIDAndStatus(numContainers int) (types.UID, *PodStatus) {
|
||||
id := types.UID(strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
name := fmt.Sprintf("cache-foo-%s", string(id))
|
||||
namespace := "ns"
|
||||
var status *PodStatus
|
||||
if numContainers > 0 {
|
||||
status = &PodStatus{ID: id, Name: name, Namespace: namespace}
|
||||
} else {
|
||||
status = &PodStatus{ID: id}
|
||||
}
|
||||
for i := 0; i < numContainers; i++ {
|
||||
status.ContainerStatuses = append(status.ContainerStatuses, &ContainerStatus{Name: string(i)})
|
||||
}
|
||||
return id, status
|
||||
}
|
||||
|
||||
func TestGetIfNewerThanWhenPodExists(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
timestamp := time.Now()
|
||||
|
||||
cases := []struct {
|
||||
cacheTime time.Time
|
||||
modified time.Time
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
// Both the global cache timestamp and the modified time are newer
|
||||
// than the timestamp.
|
||||
cacheTime: timestamp.Add(time.Second),
|
||||
modified: timestamp,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Global cache timestamp is newer, but the pod entry modified
|
||||
// time is older than the given timestamp. This means that the
|
||||
// entry is up-to-date even though it hasn't changed for a while.
|
||||
cacheTime: timestamp.Add(time.Second),
|
||||
modified: timestamp.Add(-time.Second * 10),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Global cache timestamp is older, but the pod entry modified
|
||||
// time is newer than the given timestamp. This means that the
|
||||
// entry is up-to-date but the rest of the cache are still being
|
||||
// updated.
|
||||
cacheTime: timestamp.Add(-time.Second),
|
||||
modified: timestamp.Add(time.Second * 3),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Both the global cache timestamp and the modified time are older
|
||||
// than the given timestamp.
|
||||
cacheTime: timestamp.Add(-time.Second),
|
||||
modified: timestamp.Add(-time.Second),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
podID, status := getTestPodIDAndStatus(2)
|
||||
cache.UpdateTime(c.cacheTime)
|
||||
cache.Set(podID, status, nil, c.modified)
|
||||
d := cache.getIfNewerThan(podID, timestamp)
|
||||
assert.Equal(t, c.expected, d != nil, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodNewerThanWhenPodDoesNotExist(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
cacheTime := time.Now()
|
||||
cache.UpdateTime(cacheTime)
|
||||
podID := types.UID("1234")
|
||||
|
||||
cases := []struct {
|
||||
timestamp time.Time
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
timestamp: cacheTime.Add(-time.Second),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
timestamp: cacheTime.Add(time.Second),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
d := cache.getIfNewerThan(podID, c.timestamp)
|
||||
assert.Equal(t, c.expected, d != nil, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheSetAndGet(t *testing.T) {
|
||||
cache := NewCache()
|
||||
cases := []struct {
|
||||
numContainers int
|
||||
error error
|
||||
}{
|
||||
{numContainers: 3, error: nil},
|
||||
{numContainers: 2, error: fmt.Errorf("unable to get status")},
|
||||
{numContainers: 0, error: nil},
|
||||
}
|
||||
for i, c := range cases {
|
||||
podID, status := getTestPodIDAndStatus(c.numContainers)
|
||||
cache.Set(podID, status, c.error, time.Time{})
|
||||
// Read back the status and error stored in cache and make sure they
|
||||
// match the original ones.
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus, "test[%d]", i)
|
||||
assert.Equal(t, c.error, actualErr, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheGetPodDoesNotExist(t *testing.T) {
|
||||
cache := NewCache()
|
||||
podID, status := getTestPodIDAndStatus(0)
|
||||
// If the pod does not exist in cache, cache should return an status
|
||||
// object with id filled.
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
cache := &cache{pods: map[types.UID]*data{}}
|
||||
// Write a new pod status into the cache.
|
||||
podID, status := getTestPodIDAndStatus(3)
|
||||
cache.Set(podID, status, nil, time.Time{})
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
// Delete the pod from cache, and verify that we get an empty status.
|
||||
cache.Delete(podID)
|
||||
expectedStatus := &PodStatus{ID: podID}
|
||||
actualStatus, actualErr = cache.Get(podID)
|
||||
assert.Equal(t, expectedStatus, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
}
|
||||
|
||||
func verifyNotification(t *testing.T, ch chan *data, expectNotification bool) {
|
||||
if expectNotification {
|
||||
assert.True(t, len(ch) > 0, "Did not receive notification")
|
||||
} else {
|
||||
assert.True(t, len(ch) < 1, "Should not have triggered the notification")
|
||||
}
|
||||
// Drain the channel.
|
||||
for i := 0; i < len(ch); i++ {
|
||||
<-ch
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterNotification(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
cacheTime := time.Now()
|
||||
cache.UpdateTime(cacheTime)
|
||||
|
||||
podID, status := getTestPodIDAndStatus(1)
|
||||
ch := cache.subscribe(podID, cacheTime.Add(time.Second))
|
||||
verifyNotification(t, ch, false)
|
||||
cache.Set(podID, status, nil, cacheTime.Add(time.Second))
|
||||
// The Set operation should've triggered the notification.
|
||||
verifyNotification(t, ch, true)
|
||||
|
||||
podID, _ = getTestPodIDAndStatus(1)
|
||||
|
||||
ch = cache.subscribe(podID, cacheTime.Add(time.Second))
|
||||
verifyNotification(t, ch, false)
|
||||
cache.UpdateTime(cacheTime.Add(time.Second * 2))
|
||||
// The advance of cache timestamp should've triggered the notification.
|
||||
verifyNotification(t, ch, true)
|
||||
}
|
||||
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go
generated
vendored
Normal file
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/container_gc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Specified a policy for garbage collecting containers.
|
||||
type ContainerGCPolicy struct {
|
||||
// Minimum age at which a container can be garbage collected, zero for no limit.
|
||||
MinAge time.Duration
|
||||
|
||||
// Max number of dead containers any single pod (UID, container name) pair is
|
||||
// allowed to have, less than zero for no limit.
|
||||
MaxPerPodContainer int
|
||||
|
||||
// Max number of total dead containers, less than zero for no limit.
|
||||
MaxContainers int
|
||||
}
|
||||
|
||||
// Manages garbage collection of dead containers.
|
||||
//
|
||||
// Implementation is thread-compatible.
|
||||
type ContainerGC interface {
|
||||
// Garbage collect containers.
|
||||
GarbageCollect(allSourcesReady bool) error
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Preferentially remove pod infra containers.
|
||||
type realContainerGC struct {
|
||||
// Container runtime
|
||||
runtime Runtime
|
||||
|
||||
// Policy for garbage collection.
|
||||
policy ContainerGCPolicy
|
||||
}
|
||||
|
||||
// New ContainerGC instance with the specified policy.
|
||||
func NewContainerGC(runtime Runtime, policy ContainerGCPolicy) (ContainerGC, error) {
|
||||
if policy.MinAge < 0 {
|
||||
return nil, fmt.Errorf("invalid minimum garbage collection age: %v", policy.MinAge)
|
||||
}
|
||||
|
||||
return &realContainerGC{
|
||||
runtime: runtime,
|
||||
policy: policy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cgc *realContainerGC) GarbageCollect(allSourcesReady bool) error {
|
||||
return cgc.runtime.GarbageCollect(cgc.policy, allSourcesReady)
|
||||
}
|
||||
60
vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go
generated
vendored
Normal file
60
vendor/k8s.io/kubernetes/pkg/kubelet/container/container_reference_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
// RefManager manages the references for the containers.
|
||||
// The references are used for reporting events such as creation,
|
||||
// failure, etc. This manager is thread-safe, no locks are necessary
|
||||
// for the caller.
|
||||
type RefManager struct {
|
||||
sync.RWMutex
|
||||
containerIDToRef map[ContainerID]*v1.ObjectReference
|
||||
}
|
||||
|
||||
// NewRefManager creates and returns a container reference manager
|
||||
// with empty contents.
|
||||
func NewRefManager() *RefManager {
|
||||
return &RefManager{containerIDToRef: make(map[ContainerID]*v1.ObjectReference)}
|
||||
}
|
||||
|
||||
// SetRef stores a reference to a pod's container, associating it with the given container ID.
|
||||
func (c *RefManager) SetRef(id ContainerID, ref *v1.ObjectReference) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.containerIDToRef[id] = ref
|
||||
}
|
||||
|
||||
// ClearRef forgets the given container id and its associated container reference.
|
||||
func (c *RefManager) ClearRef(id ContainerID) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
delete(c.containerIDToRef, id)
|
||||
}
|
||||
|
||||
// GetRef returns the container reference of the given ID, or (nil, false) if none is stored.
|
||||
func (c *RefManager) GetRef(id ContainerID) (ref *v1.ObjectReference, ok bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
ref, ok = c.containerIDToRef[id]
|
||||
return ref, ok
|
||||
}
|
||||
263
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go
generated
vendored
Normal file
263
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers.go
generated
vendored
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/ioutils"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
hashutil "k8s.io/kubernetes/pkg/util/hash"
|
||||
"k8s.io/kubernetes/third_party/forked/golang/expansion"
|
||||
)
|
||||
|
||||
// HandlerRunner runs a lifecycle handler for a container.
|
||||
type HandlerRunner interface {
|
||||
Run(containerID ContainerID, pod *v1.Pod, container *v1.Container, handler *v1.Handler) (string, error)
|
||||
}
|
||||
|
||||
// RuntimeHelper wraps kubelet to make container runtime
|
||||
// able to get necessary informations like the RunContainerOptions, DNS settings.
|
||||
type RuntimeHelper interface {
|
||||
GenerateRunContainerOptions(pod *v1.Pod, container *v1.Container, podIP string) (*RunContainerOptions, error)
|
||||
GetClusterDNS(pod *v1.Pod) (dnsServers []string, dnsSearches []string, err error)
|
||||
GetPodDir(podUID types.UID) string
|
||||
GeneratePodHostNameAndDomain(pod *v1.Pod) (hostname string, hostDomain string, err error)
|
||||
// GetExtraSupplementalGroupsForPod returns a list of the extra
|
||||
// supplemental groups for the Pod. These extra supplemental groups come
|
||||
// from annotations on persistent volumes that the pod depends on.
|
||||
GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64
|
||||
}
|
||||
|
||||
// ShouldContainerBeRestarted checks whether a container needs to be restarted.
|
||||
// TODO(yifan): Think about how to refactor this.
|
||||
func ShouldContainerBeRestarted(container *v1.Container, pod *v1.Pod, podStatus *PodStatus) bool {
|
||||
// Get latest container status.
|
||||
status := podStatus.FindContainerStatusByName(container.Name)
|
||||
// If the container was never started before, we should start it.
|
||||
// NOTE(random-liu): If all historical containers were GC'd, we'll also return true here.
|
||||
if status == nil {
|
||||
return true
|
||||
}
|
||||
// Check whether container is running
|
||||
if status.State == ContainerStateRunning {
|
||||
return false
|
||||
}
|
||||
// Always restart container in unknown state now
|
||||
if status.State == ContainerStateUnknown {
|
||||
return true
|
||||
}
|
||||
// Check RestartPolicy for dead container
|
||||
if pod.Spec.RestartPolicy == v1.RestartPolicyNever {
|
||||
glog.V(4).Infof("Already ran container %q of pod %q, do nothing", container.Name, format.Pod(pod))
|
||||
return false
|
||||
}
|
||||
if pod.Spec.RestartPolicy == v1.RestartPolicyOnFailure {
|
||||
// Check the exit code.
|
||||
if status.ExitCode == 0 {
|
||||
glog.V(4).Infof("Already successfully ran container %q of pod %q, do nothing", container.Name, format.Pod(pod))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HashContainer returns the hash of the container. It is used to compare
|
||||
// the running container with its desired spec.
|
||||
func HashContainer(container *v1.Container) uint64 {
|
||||
hash := adler32.New()
|
||||
hashutil.DeepHashObject(hash, *container)
|
||||
return uint64(hash.Sum32())
|
||||
}
|
||||
|
||||
// EnvVarsToMap constructs a map of environment name to value from a slice
|
||||
// of env vars.
|
||||
func EnvVarsToMap(envs []EnvVar) map[string]string {
|
||||
result := map[string]string{}
|
||||
for _, env := range envs {
|
||||
result[env.Name] = env.Value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func ExpandContainerCommandAndArgs(container *v1.Container, envs []EnvVar) (command []string, args []string) {
|
||||
mapping := expansion.MappingFuncFor(EnvVarsToMap(envs))
|
||||
|
||||
if len(container.Command) != 0 {
|
||||
for _, cmd := range container.Command {
|
||||
command = append(command, expansion.Expand(cmd, mapping))
|
||||
}
|
||||
}
|
||||
|
||||
if len(container.Args) != 0 {
|
||||
for _, arg := range container.Args {
|
||||
args = append(args, expansion.Expand(arg, mapping))
|
||||
}
|
||||
}
|
||||
|
||||
return command, args
|
||||
}
|
||||
|
||||
// Create an event recorder to record object's event except implicitly required container's, like infra container.
|
||||
func FilterEventRecorder(recorder record.EventRecorder) record.EventRecorder {
|
||||
return &innerEventRecorder{
|
||||
recorder: recorder,
|
||||
}
|
||||
}
|
||||
|
||||
type innerEventRecorder struct {
|
||||
recorder record.EventRecorder
|
||||
}
|
||||
|
||||
func (irecorder *innerEventRecorder) shouldRecordEvent(object runtime.Object) (*v1.ObjectReference, bool) {
|
||||
if object == nil {
|
||||
return nil, false
|
||||
}
|
||||
if ref, ok := object.(*v1.ObjectReference); ok {
|
||||
if !strings.HasPrefix(ref.FieldPath, ImplicitContainerPrefix) {
|
||||
return ref, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (irecorder *innerEventRecorder) Event(object runtime.Object, eventtype, reason, message string) {
|
||||
if ref, ok := irecorder.shouldRecordEvent(object); ok {
|
||||
irecorder.recorder.Event(ref, eventtype, reason, message)
|
||||
}
|
||||
}
|
||||
|
||||
func (irecorder *innerEventRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
|
||||
if ref, ok := irecorder.shouldRecordEvent(object); ok {
|
||||
irecorder.recorder.Eventf(ref, eventtype, reason, messageFmt, args...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (irecorder *innerEventRecorder) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) {
|
||||
if ref, ok := irecorder.shouldRecordEvent(object); ok {
|
||||
irecorder.recorder.PastEventf(ref, timestamp, eventtype, reason, messageFmt, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Pod must not be nil.
|
||||
func IsHostNetworkPod(pod *v1.Pod) bool {
|
||||
return pod.Spec.HostNetwork
|
||||
}
|
||||
|
||||
// TODO(random-liu): Convert PodStatus to running Pod, should be deprecated soon
|
||||
func ConvertPodStatusToRunningPod(runtimeName string, podStatus *PodStatus) Pod {
|
||||
runningPod := Pod{
|
||||
ID: podStatus.ID,
|
||||
Name: podStatus.Name,
|
||||
Namespace: podStatus.Namespace,
|
||||
}
|
||||
for _, containerStatus := range podStatus.ContainerStatuses {
|
||||
if containerStatus.State != ContainerStateRunning {
|
||||
continue
|
||||
}
|
||||
container := &Container{
|
||||
ID: containerStatus.ID,
|
||||
Name: containerStatus.Name,
|
||||
Image: containerStatus.Image,
|
||||
ImageID: containerStatus.ImageID,
|
||||
Hash: containerStatus.Hash,
|
||||
State: containerStatus.State,
|
||||
}
|
||||
runningPod.Containers = append(runningPod.Containers, container)
|
||||
}
|
||||
|
||||
// Populate sandboxes in kubecontainer.Pod
|
||||
for _, sandbox := range podStatus.SandboxStatuses {
|
||||
runningPod.Sandboxes = append(runningPod.Sandboxes, &Container{
|
||||
ID: ContainerID{Type: runtimeName, ID: *sandbox.Id},
|
||||
State: SandboxToContainerState(*sandbox.State),
|
||||
})
|
||||
}
|
||||
return runningPod
|
||||
}
|
||||
|
||||
// sandboxToContainerState converts runtimeApi.PodSandboxState to
|
||||
// kubecontainer.ContainerState.
|
||||
// This is only needed because we need to return sandboxes as if they were
|
||||
// kubecontainer.Containers to avoid substantial changes to PLEG.
|
||||
// TODO: Remove this once it becomes obsolete.
|
||||
func SandboxToContainerState(state runtimeapi.PodSandboxState) ContainerState {
|
||||
switch state {
|
||||
case runtimeapi.PodSandboxState_SANDBOX_READY:
|
||||
return ContainerStateRunning
|
||||
case runtimeapi.PodSandboxState_SANDBOX_NOTREADY:
|
||||
return ContainerStateExited
|
||||
}
|
||||
return ContainerStateUnknown
|
||||
}
|
||||
|
||||
// FormatPod returns a string representing a pod in a human readable format,
|
||||
// with pod UID as part of the string.
|
||||
func FormatPod(pod *Pod) string {
|
||||
// Use underscore as the delimiter because it is not allowed in pod name
|
||||
// (DNS subdomain format), while allowed in the container name format.
|
||||
return fmt.Sprintf("%s_%s(%s)", pod.Name, pod.Namespace, pod.ID)
|
||||
}
|
||||
|
||||
type containerCommandRunnerWrapper struct {
|
||||
DirectStreamingRuntime
|
||||
}
|
||||
|
||||
var _ ContainerCommandRunner = &containerCommandRunnerWrapper{}
|
||||
|
||||
func DirectStreamingRunner(runtime DirectStreamingRuntime) ContainerCommandRunner {
|
||||
return &containerCommandRunnerWrapper{runtime}
|
||||
}
|
||||
|
||||
func (r *containerCommandRunnerWrapper) RunInContainer(id ContainerID, cmd []string, timeout time.Duration) ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
output := ioutils.WriteCloserWrapper(&buffer)
|
||||
err := r.ExecInContainer(id, cmd, nil, output, output, false, nil, timeout)
|
||||
// Even if err is non-nil, there still may be output (e.g. the exec wrote to stdout or stderr but
|
||||
// the command returned a nonzero exit code). Therefore, always return the output along with the
|
||||
// error.
|
||||
return buffer.Bytes(), err
|
||||
}
|
||||
|
||||
// GetContainerSpec gets the container spec by containerName.
|
||||
func GetContainerSpec(pod *v1.Pod, containerName string) *v1.Container {
|
||||
for i, c := range pod.Spec.Containers {
|
||||
if containerName == c.Name {
|
||||
return &pod.Spec.Containers[i]
|
||||
}
|
||||
}
|
||||
for i, c := range pod.Spec.InitContainers {
|
||||
if containerName == c.Name {
|
||||
return &pod.Spec.InitContainers[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
213
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers_test.go
generated
vendored
Normal file
213
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
func TestEnvVarsToMap(t *testing.T) {
|
||||
vars := []EnvVar{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "zoo",
|
||||
Value: "baz",
|
||||
},
|
||||
}
|
||||
|
||||
varMap := EnvVarsToMap(vars)
|
||||
|
||||
if e, a := len(vars), len(varMap); e != a {
|
||||
t.Errorf("Unexpected map length; expected: %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if a := varMap["foo"]; a != "bar" {
|
||||
t.Errorf("Unexpected value of key 'foo': %v", a)
|
||||
}
|
||||
|
||||
if a := varMap["zoo"]; a != "baz" {
|
||||
t.Errorf("Unexpected value of key 'zoo': %v", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandCommandAndArgs(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
container *v1.Container
|
||||
envs []EnvVar
|
||||
expectedCommand []string
|
||||
expectedArgs []string
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
container: &v1.Container{},
|
||||
},
|
||||
{
|
||||
name: "command expanded",
|
||||
container: &v1.Container{
|
||||
Command: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "zoo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "boo",
|
||||
},
|
||||
},
|
||||
expectedCommand: []string{"foo", "zoo", "boo"},
|
||||
},
|
||||
{
|
||||
name: "args expanded",
|
||||
container: &v1.Container{
|
||||
Args: []string{"zap", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "hap",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "trap",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"zap", "hap", "trap"},
|
||||
},
|
||||
{
|
||||
name: "both expanded",
|
||||
container: &v1.Container{
|
||||
Command: []string{"$(VAR_TEST2)--$(VAR_TEST)", "foo", "$(VAR_TEST3)"},
|
||||
Args: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "zoo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "boo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST3",
|
||||
Value: "roo",
|
||||
},
|
||||
},
|
||||
expectedCommand: []string{"boo--zoo", "foo", "roo"},
|
||||
expectedArgs: []string{"foo", "zoo", "boo"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualCommand, actualArgs := ExpandContainerCommandAndArgs(tc.container, tc.envs)
|
||||
|
||||
if e, a := tc.expectedCommand, actualCommand; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: unexpected command; expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
|
||||
if e, a := tc.expectedArgs, actualArgs; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: unexpected args; expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldContainerBeRestarted(t *testing.T) {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
UID: "12345678",
|
||||
Name: "foo",
|
||||
Namespace: "new",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "no-history"},
|
||||
{Name: "alive"},
|
||||
{Name: "succeed"},
|
||||
{Name: "failed"},
|
||||
{Name: "unknown"},
|
||||
},
|
||||
},
|
||||
}
|
||||
podStatus := &PodStatus{
|
||||
ID: pod.UID,
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
ContainerStatuses: []*ContainerStatus{
|
||||
{
|
||||
Name: "alive",
|
||||
State: ContainerStateRunning,
|
||||
},
|
||||
{
|
||||
Name: "succeed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 0,
|
||||
},
|
||||
{
|
||||
Name: "failed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 1,
|
||||
},
|
||||
{
|
||||
Name: "alive",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 2,
|
||||
},
|
||||
{
|
||||
Name: "unknown",
|
||||
State: ContainerStateUnknown,
|
||||
},
|
||||
{
|
||||
Name: "failed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
policies := []v1.RestartPolicy{
|
||||
v1.RestartPolicyNever,
|
||||
v1.RestartPolicyOnFailure,
|
||||
v1.RestartPolicyAlways,
|
||||
}
|
||||
expected := map[string][]bool{
|
||||
"no-history": {true, true, true},
|
||||
"alive": {false, false, false},
|
||||
"succeed": {false, false, true},
|
||||
"failed": {false, true, true},
|
||||
"unknown": {true, true, true},
|
||||
}
|
||||
for _, c := range pod.Spec.Containers {
|
||||
for i, policy := range policies {
|
||||
pod.Spec.RestartPolicy = policy
|
||||
e := expected[c.Name][i]
|
||||
r := ShouldContainerBeRestarted(&c, pod, podStatus)
|
||||
if r != e {
|
||||
t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
|
||||
c.Name, policy, e, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/pkg/kubelet/container/os.go
generated
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OSInterface collects system level operations that need to be mocked out
|
||||
// during tests.
|
||||
type OSInterface interface {
|
||||
MkdirAll(path string, perm os.FileMode) error
|
||||
Symlink(oldname string, newname string) error
|
||||
Stat(path string) (os.FileInfo, error)
|
||||
Remove(path string) error
|
||||
RemoveAll(path string) error
|
||||
Create(path string) (*os.File, error)
|
||||
Hostname() (name string, err error)
|
||||
Chtimes(path string, atime time.Time, mtime time.Time) error
|
||||
Pipe() (r *os.File, w *os.File, err error)
|
||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
Glob(pattern string) ([]string, error)
|
||||
}
|
||||
|
||||
// RealOS is used to dispatch the real system level operations.
|
||||
type RealOS struct{}
|
||||
|
||||
// MkDir will will call os.Mkdir to create a directory.
|
||||
func (RealOS) MkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// Symlink will call os.Symlink to create a symbolic link.
|
||||
func (RealOS) Symlink(oldname string, newname string) error {
|
||||
return os.Symlink(oldname, newname)
|
||||
}
|
||||
|
||||
// Stat will call os.Stat to get the FileInfo for a given path
|
||||
func (RealOS) Stat(path string) (os.FileInfo, error) {
|
||||
return os.Stat(path)
|
||||
}
|
||||
|
||||
// Remove will call os.Remove to remove the path.
|
||||
func (RealOS) Remove(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// RemoveAll will call os.RemoveAll to remove the path and its children.
|
||||
func (RealOS) RemoveAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// Create will call os.Create to create and return a file
|
||||
// at path.
|
||||
func (RealOS) Create(path string) (*os.File, error) {
|
||||
return os.Create(path)
|
||||
}
|
||||
|
||||
// Hostname will call os.Hostname to return the hostname.
|
||||
func (RealOS) Hostname() (name string, err error) {
|
||||
return os.Hostname()
|
||||
}
|
||||
|
||||
// Chtimes will call os.Chtimes to change the atime and mtime of the path
|
||||
func (RealOS) Chtimes(path string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(path, atime, mtime)
|
||||
}
|
||||
|
||||
// Pipe will call os.Pipe to return a connected pair of pipe.
|
||||
func (RealOS) Pipe() (r *os.File, w *os.File, err error) {
|
||||
return os.Pipe()
|
||||
}
|
||||
|
||||
// ReadDir will call ioutil.ReadDir to return the files under the directory.
|
||||
func (RealOS) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
return ioutil.ReadDir(dirname)
|
||||
}
|
||||
|
||||
// Glob will call filepath.Glob to return the names of all files matching
|
||||
// pattern.
|
||||
func (RealOS) Glob(pattern string) ([]string, error) {
|
||||
return filepath.Glob(pattern)
|
||||
}
|
||||
30
vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// +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 container
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/kr/pty"
|
||||
)
|
||||
|
||||
func StartPty(c *exec.Cmd) (*os.File, error) {
|
||||
return pty.Start(c)
|
||||
}
|
||||
28
vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/kubelet/container/pty_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// +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 container
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func StartPty(c *exec.Cmd) (pty *os.File, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
71
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go
generated
vendored
Normal file
71
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
var ImplicitContainerPrefix string = "implicitly required container "
|
||||
|
||||
// GenerateContainerRef returns an *v1.ObjectReference which references the given container
|
||||
// within the given pod. Returns an error if the reference can't be constructed or the
|
||||
// container doesn't actually belong to the pod.
|
||||
//
|
||||
// This function will return an error if the provided Pod does not have a selfLink,
|
||||
// but we expect selfLink to be populated at all call sites for the function.
|
||||
func GenerateContainerRef(pod *v1.Pod, container *v1.Container) (*v1.ObjectReference, error) {
|
||||
fieldPath, err := fieldPath(pod, container)
|
||||
if err != nil {
|
||||
// TODO: figure out intelligent way to refer to containers that we implicitly
|
||||
// start (like the pod infra container). This is not a good way, ugh.
|
||||
fieldPath = ImplicitContainerPrefix + container.Name
|
||||
}
|
||||
ref, err := v1.GetPartialReference(pod, fieldPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
// fieldPath returns a fieldPath locating container within pod.
|
||||
// Returns an error if the container isn't part of the pod.
|
||||
func fieldPath(pod *v1.Pod, container *v1.Container) (string, error) {
|
||||
for i := range pod.Spec.Containers {
|
||||
here := &pod.Spec.Containers[i]
|
||||
if here.Name == container.Name {
|
||||
if here.Name == "" {
|
||||
return fmt.Sprintf("spec.containers[%d]", i), nil
|
||||
} else {
|
||||
return fmt.Sprintf("spec.containers{%s}", here.Name), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.InitContainers {
|
||||
here := &pod.Spec.InitContainers[i]
|
||||
if here.Name == container.Name {
|
||||
if here.Name == "" {
|
||||
return fmt.Sprintf("spec.initContainers[%d]", i), nil
|
||||
} else {
|
||||
return fmt.Sprintf("spec.initContainers{%s}", here.Name), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("container %#v not found in pod %#v", container, pod)
|
||||
}
|
||||
212
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref_test.go
generated
vendored
Normal file
212
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestFieldPath(t *testing.T) {
|
||||
pod := &v1.Pod{Spec: v1.PodSpec{Containers: []v1.Container{
|
||||
{Name: "foo"},
|
||||
{Name: "bar"},
|
||||
{Name: ""},
|
||||
{Name: "baz"},
|
||||
}}}
|
||||
table := map[string]struct {
|
||||
pod *v1.Pod
|
||||
container *v1.Container
|
||||
path string
|
||||
success bool
|
||||
}{
|
||||
"basic": {pod, &v1.Container{Name: "foo"}, "spec.containers{foo}", true},
|
||||
"basic2": {pod, &v1.Container{Name: "baz"}, "spec.containers{baz}", true},
|
||||
"emptyName": {pod, &v1.Container{Name: ""}, "spec.containers[2]", true},
|
||||
"basicSamePointer": {pod, &pod.Spec.Containers[0], "spec.containers{foo}", true},
|
||||
"missing": {pod, &v1.Container{Name: "qux"}, "", false},
|
||||
}
|
||||
|
||||
for name, item := range table {
|
||||
res, err := fieldPath(item.pod, item.container)
|
||||
if item.success == false {
|
||||
if err == nil {
|
||||
t.Errorf("%v: unexpected non-error", name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if e, a := item.path, res; e != a {
|
||||
t.Errorf("%v: wanted %v, got %v", name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateContainerRef(t *testing.T) {
|
||||
var (
|
||||
okPod = v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
SelfLink: "/api/" + registered.GroupOrDie(v1.GroupName).GroupVersion.String() + "/pods/foo",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "by-name",
|
||||
},
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
noSelfLinkPod = okPod
|
||||
defaultedSelfLinkPod = okPod
|
||||
)
|
||||
noSelfLinkPod.Kind = ""
|
||||
noSelfLinkPod.APIVersion = ""
|
||||
noSelfLinkPod.ObjectMeta.SelfLink = ""
|
||||
defaultedSelfLinkPod.ObjectMeta.SelfLink = "/api/" + registered.GroupOrDie(v1.GroupName).GroupVersion.String() + "/pods/ok"
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
container *v1.Container
|
||||
expected *v1.ObjectReference
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
name: "by-name",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{
|
||||
Name: "by-name",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers{by-name}",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "no-name",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers[1]",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "no-selflink",
|
||||
pod: &noSelfLinkPod,
|
||||
container: &v1.Container{},
|
||||
expected: nil,
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
name: "defaulted-selflink",
|
||||
pod: &defaultedSelfLinkPod,
|
||||
container: &v1.Container{
|
||||
Name: "by-name",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers{by-name}",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "implicitly-required",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{
|
||||
Name: "net",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: registered.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: "implicitly required container net",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actual, err := GenerateContainerRef(tc.pod, tc.container)
|
||||
if err != nil {
|
||||
if tc.success {
|
||||
t.Errorf("%v: unexpected error: %v", tc.name, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !tc.success {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if e, a := tc.expected.Kind, actual.Kind; e != a {
|
||||
t.Errorf("%v: kind: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.APIVersion, actual.APIVersion; e != a {
|
||||
t.Errorf("%v: apiVersion: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.Name, actual.Name; e != a {
|
||||
t.Errorf("%v: name: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.Namespace, actual.Namespace; e != a {
|
||||
t.Errorf("%v: namespace: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.UID, actual.UID; e != a {
|
||||
t.Errorf("%v: uid: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.ResourceVersion, actual.ResourceVersion; e != a {
|
||||
t.Errorf("%v: kind: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
46
vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubelet/container/resize.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/term"
|
||||
)
|
||||
|
||||
// handleResizing spawns a goroutine that processes the resize channel, calling resizeFunc for each
|
||||
// term.Size received from the channel. The resize channel must be closed elsewhere to stop the
|
||||
// goroutine.
|
||||
func HandleResizing(resize <-chan term.Size, resizeFunc func(size term.Size)) {
|
||||
if resize == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer runtime.HandleCrash()
|
||||
|
||||
for {
|
||||
size, ok := <-resize
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if size.Height < 1 || size.Width < 1 {
|
||||
continue
|
||||
}
|
||||
resizeFunc(size)
|
||||
}
|
||||
}()
|
||||
}
|
||||
627
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go
generated
vendored
Normal file
627
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
96
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(yifan): Maybe set the them as parameters for NewCache().
|
||||
defaultCachePeriod = time.Second * 2
|
||||
)
|
||||
|
||||
type RuntimeCache interface {
|
||||
GetPods() ([]*Pod, error)
|
||||
ForceUpdateIfOlder(time.Time) error
|
||||
}
|
||||
|
||||
type podsGetter interface {
|
||||
GetPods(bool) ([]*Pod, error)
|
||||
}
|
||||
|
||||
// NewRuntimeCache creates a container runtime cache.
|
||||
func NewRuntimeCache(getter podsGetter) (RuntimeCache, error) {
|
||||
return &runtimeCache{
|
||||
getter: getter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// runtimeCache caches a list of pods. It records a timestamp (cacheTime) right
|
||||
// before updating the pods, so the timestamp is at most as new as the pods
|
||||
// (and can be slightly older). The timestamp always moves forward. Callers are
|
||||
// expected not to modify the pods returned from GetPods.
|
||||
type runtimeCache struct {
|
||||
sync.Mutex
|
||||
// The underlying container runtime used to update the cache.
|
||||
getter podsGetter
|
||||
// Last time when cache was updated.
|
||||
cacheTime time.Time
|
||||
// The content of the cache.
|
||||
pods []*Pod
|
||||
}
|
||||
|
||||
// GetPods returns the cached pods if they are not outdated; otherwise, it
|
||||
// retrieves the latest pods and return them.
|
||||
func (r *runtimeCache) GetPods() ([]*Pod, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
if time.Since(r.cacheTime) > defaultCachePeriod {
|
||||
if err := r.updateCache(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r.pods, nil
|
||||
}
|
||||
|
||||
func (r *runtimeCache) ForceUpdateIfOlder(minExpectedCacheTime time.Time) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
if r.cacheTime.Before(minExpectedCacheTime) {
|
||||
return r.updateCache()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runtimeCache) updateCache() error {
|
||||
pods, timestamp, err := r.getPodsWithTimestamp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.pods, r.cacheTime = pods, timestamp
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPodsWithTimestamp records a timestamp and retrieves pods from the getter.
|
||||
func (r *runtimeCache) getPodsWithTimestamp() ([]*Pod, time.Time, error) {
|
||||
// Always record the timestamp before getting the pods to avoid stale pods.
|
||||
timestamp := time.Now()
|
||||
pods, err := r.getter.GetPods(false)
|
||||
return pods, timestamp, err
|
||||
}
|
||||
45
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_fake.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
// TestRunTimeCache embeds runtimeCache with some additional methods for testing.
|
||||
// It must be declared in the container package to have visibility to runtimeCache.
|
||||
// It cannot be in a "..._test.go" file in order for runtime_cache_test.go to have cross-package visibility to it.
|
||||
// (cross-package declarations in test files cannot be used from dot imports if this package is vendored)
|
||||
type TestRuntimeCache struct {
|
||||
runtimeCache
|
||||
}
|
||||
|
||||
func (r *TestRuntimeCache) UpdateCacheWithLock() error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
return r.updateCache()
|
||||
}
|
||||
|
||||
func (r *TestRuntimeCache) GetCachedPods() []*Pod {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
return r.pods
|
||||
}
|
||||
|
||||
func NewTestRuntimeCache(getter podsGetter) *TestRuntimeCache {
|
||||
return &TestRuntimeCache{
|
||||
runtimeCache: runtimeCache{
|
||||
getter: getter,
|
||||
},
|
||||
}
|
||||
}
|
||||
74
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_test.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
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 container_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
ctest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
)
|
||||
|
||||
func comparePods(t *testing.T, expected []*ctest.FakePod, actual []*Pod) {
|
||||
if len(expected) != len(actual) {
|
||||
t.Errorf("expected %d pods, got %d instead", len(expected), len(actual))
|
||||
}
|
||||
for i := range expected {
|
||||
if !reflect.DeepEqual(expected[i].Pod, actual[i]) {
|
||||
t.Errorf("expected %#v, got %#v", expected[i].Pod, actual[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPods(t *testing.T) {
|
||||
runtime := &ctest.FakeRuntime{}
|
||||
expected := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}, {Pod: &Pod{ID: "2222"}}, {Pod: &Pod{ID: "3333"}}}
|
||||
runtime.PodList = expected
|
||||
cache := NewTestRuntimeCache(runtime)
|
||||
actual, err := cache.GetPods()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
comparePods(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestForceUpdateIfOlder(t *testing.T) {
|
||||
runtime := &ctest.FakeRuntime{}
|
||||
cache := NewTestRuntimeCache(runtime)
|
||||
|
||||
// Cache old pods.
|
||||
oldpods := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}}
|
||||
runtime.PodList = oldpods
|
||||
cache.UpdateCacheWithLock()
|
||||
|
||||
// Update the runtime to new pods.
|
||||
newpods := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}, {Pod: &Pod{ID: "2222"}}, {Pod: &Pod{ID: "3333"}}}
|
||||
runtime.PodList = newpods
|
||||
|
||||
// An older timestamp should not force an update.
|
||||
cache.ForceUpdateIfOlder(time.Now().Add(-20 * time.Minute))
|
||||
actual := cache.GetCachedPods()
|
||||
comparePods(t, oldpods, actual)
|
||||
|
||||
// A newer timestamp should force an update.
|
||||
cache.ForceUpdateIfOlder(time.Now().Add(20 * time.Second))
|
||||
actual = cache.GetCachedPods()
|
||||
comparePods(t, newpods, actual)
|
||||
}
|
||||
128
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go
generated
vendored
Normal file
128
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
// TODO(random-liu): We need to better organize runtime errors for introspection.
|
||||
|
||||
// Container Terminated and Kubelet is backing off the restart
|
||||
var ErrCrashLoopBackOff = errors.New("CrashLoopBackOff")
|
||||
|
||||
var (
|
||||
// ErrContainerNotFound returned when a container in the given pod with the
|
||||
// given container name was not found, amongst those managed by the kubelet.
|
||||
ErrContainerNotFound = errors.New("no matching container")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRunContainer = errors.New("RunContainerError")
|
||||
ErrKillContainer = errors.New("KillContainerError")
|
||||
ErrVerifyNonRoot = errors.New("VerifyNonRootError")
|
||||
ErrRunInitContainer = errors.New("RunInitContainerError")
|
||||
ErrCreatePodSandbox = errors.New("CreatePodSandboxError")
|
||||
ErrConfigPodSandbox = errors.New("ConfigPodSandboxError")
|
||||
ErrKillPodSandbox = errors.New("KillPodSandboxError")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSetupNetwork = errors.New("SetupNetworkError")
|
||||
ErrTeardownNetwork = errors.New("TeardownNetworkError")
|
||||
)
|
||||
|
||||
// SyncAction indicates different kind of actions in SyncPod() and KillPod(). Now there are only actions
|
||||
// about start/kill container and setup/teardown network.
|
||||
type SyncAction string
|
||||
|
||||
const (
|
||||
StartContainer SyncAction = "StartContainer"
|
||||
KillContainer SyncAction = "KillContainer"
|
||||
SetupNetwork SyncAction = "SetupNetwork"
|
||||
TeardownNetwork SyncAction = "TeardownNetwork"
|
||||
InitContainer SyncAction = "InitContainer"
|
||||
CreatePodSandbox SyncAction = "CreatePodSandbox"
|
||||
ConfigPodSandbox SyncAction = "ConfigPodSandbox"
|
||||
KillPodSandbox SyncAction = "KillPodSandbox"
|
||||
)
|
||||
|
||||
// SyncResult is the result of sync action.
|
||||
type SyncResult struct {
|
||||
// The associated action of the result
|
||||
Action SyncAction
|
||||
// The target of the action, now the target can only be:
|
||||
// * Container: Target should be container name
|
||||
// * Network: Target is useless now, we just set it as pod full name now
|
||||
Target interface{}
|
||||
// Brief error reason
|
||||
Error error
|
||||
// Human readable error reason
|
||||
Message string
|
||||
}
|
||||
|
||||
// NewSyncResult generates new SyncResult with specific Action and Target
|
||||
func NewSyncResult(action SyncAction, target interface{}) *SyncResult {
|
||||
return &SyncResult{Action: action, Target: target}
|
||||
}
|
||||
|
||||
// Fail fails the SyncResult with specific error and message
|
||||
func (r *SyncResult) Fail(err error, msg string) {
|
||||
r.Error, r.Message = err, msg
|
||||
}
|
||||
|
||||
// PodSyncResult is the summary result of SyncPod() and KillPod()
|
||||
type PodSyncResult struct {
|
||||
// Result of different sync actions
|
||||
SyncResults []*SyncResult
|
||||
// Error encountered in SyncPod() and KillPod() that is not already included in SyncResults
|
||||
SyncError error
|
||||
}
|
||||
|
||||
// AddSyncResult adds multiple SyncResult to current PodSyncResult
|
||||
func (p *PodSyncResult) AddSyncResult(result ...*SyncResult) {
|
||||
p.SyncResults = append(p.SyncResults, result...)
|
||||
}
|
||||
|
||||
// AddPodSyncResult merges a PodSyncResult to current one
|
||||
func (p *PodSyncResult) AddPodSyncResult(result PodSyncResult) {
|
||||
p.AddSyncResult(result.SyncResults...)
|
||||
p.SyncError = result.SyncError
|
||||
}
|
||||
|
||||
// Fail fails the PodSyncResult with an error occurred in SyncPod() and KillPod() itself
|
||||
func (p *PodSyncResult) Fail(err error) {
|
||||
p.SyncError = err
|
||||
}
|
||||
|
||||
// Error returns an error summarizing all the errors in PodSyncResult
|
||||
func (p *PodSyncResult) Error() error {
|
||||
errlist := []error{}
|
||||
if p.SyncError != nil {
|
||||
errlist = append(errlist, fmt.Errorf("failed to SyncPod: %v\n", p.SyncError))
|
||||
}
|
||||
for _, result := range p.SyncResults {
|
||||
if result.Error != nil {
|
||||
errlist = append(errlist, fmt.Errorf("failed to %q for %q with %v: %q\n", result.Action, result.Target,
|
||||
result.Error, result.Message))
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errlist)
|
||||
}
|
||||
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result_test.go
generated
vendored
Normal file
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPodSyncResult(t *testing.T) {
|
||||
okResults := []*SyncResult{
|
||||
NewSyncResult(StartContainer, "container_0"),
|
||||
NewSyncResult(SetupNetwork, "pod"),
|
||||
}
|
||||
errResults := []*SyncResult{
|
||||
NewSyncResult(KillContainer, "container_1"),
|
||||
NewSyncResult(TeardownNetwork, "pod"),
|
||||
}
|
||||
errResults[0].Fail(errors.New("error_0"), "message_0")
|
||||
errResults[1].Fail(errors.New("error_1"), "message_1")
|
||||
|
||||
// If the PodSyncResult doesn't contain error result, it should not be error
|
||||
result := PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
if result.Error() != nil {
|
||||
t.Errorf("PodSyncResult should not be error: %v", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult contains error result, it should be error
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.AddSyncResult(errResults...)
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult is failed, it should be error
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.Fail(errors.New("error"))
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult is added an error PodSyncResult, it should be error
|
||||
errResult := PodSyncResult{}
|
||||
errResult.AddSyncResult(errResults...)
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.AddPodSyncResult(errResult)
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
}
|
||||
33
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/BUILD
generated
vendored
Normal file
33
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
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 = [
|
||||
"fake_cache.go",
|
||||
"fake_runtime.go",
|
||||
"mockfileinfo.go",
|
||||
"os.go",
|
||||
"runtime_mock.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/flowcontrol:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//vendor:github.com/golang/mock/gomock",
|
||||
"//vendor:github.com/stretchr/testify/mock",
|
||||
],
|
||||
)
|
||||
49
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/fake_cache.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/fake_cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type fakeCache struct {
|
||||
runtime container.Runtime
|
||||
}
|
||||
|
||||
func NewFakeCache(runtime container.Runtime) container.Cache {
|
||||
return &fakeCache{runtime: runtime}
|
||||
}
|
||||
|
||||
func (c *fakeCache) Get(id types.UID) (*container.PodStatus, error) {
|
||||
return c.runtime.GetPodStatus(id, "", "")
|
||||
}
|
||||
|
||||
func (c *fakeCache) GetNewerThan(id types.UID, minTime time.Time) (*container.PodStatus, error) {
|
||||
return c.Get(id)
|
||||
}
|
||||
|
||||
func (c *fakeCache) Set(id types.UID, status *container.PodStatus, err error, timestamp time.Time) {
|
||||
}
|
||||
|
||||
func (c *fakeCache) Delete(id types.UID) {
|
||||
}
|
||||
|
||||
func (c *fakeCache) UpdateTime(_ time.Time) {
|
||||
}
|
||||
500
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/fake_runtime.go
generated
vendored
Normal file
500
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/fake_runtime.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
109
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/mockfileinfo.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/mockfileinfo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Generated via: mockgen os FileInfo
|
||||
// Edited to include required boilerplate
|
||||
// Source: os (interfaces: FileInfo)
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
os "os"
|
||||
time "time"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// Mock of FileInfo interface
|
||||
type MockFileInfo struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *_MockFileInfoRecorder
|
||||
}
|
||||
|
||||
// Recorder for MockFileInfo (not exported)
|
||||
type _MockFileInfoRecorder struct {
|
||||
mock *MockFileInfo
|
||||
}
|
||||
|
||||
func NewMockFileInfo(ctrl *gomock.Controller) *MockFileInfo {
|
||||
mock := &MockFileInfo{ctrl: ctrl}
|
||||
mock.recorder = &_MockFileInfoRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) EXPECT() *_MockFileInfoRecorder {
|
||||
return _m.recorder
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) IsDir() bool {
|
||||
ret := _m.ctrl.Call(_m, "IsDir")
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) IsDir() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "IsDir")
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) ModTime() time.Time {
|
||||
ret := _m.ctrl.Call(_m, "ModTime")
|
||||
ret0, _ := ret[0].(time.Time)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) ModTime() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ModTime")
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) Mode() os.FileMode {
|
||||
ret := _m.ctrl.Call(_m, "Mode")
|
||||
ret0, _ := ret[0].(os.FileMode)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) Mode() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Mode")
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) Name() string {
|
||||
ret := _m.ctrl.Call(_m, "Name")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) Name() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Name")
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) Size() int64 {
|
||||
ret := _m.ctrl.Call(_m, "Size")
|
||||
ret0, _ := ret[0].(int64)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) Size() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Size")
|
||||
}
|
||||
|
||||
func (_m *MockFileInfo) Sys() interface{} {
|
||||
ret := _m.ctrl.Call(_m, "Sys")
|
||||
ret0, _ := ret[0].(interface{})
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockFileInfoRecorder) Sys() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Sys")
|
||||
}
|
||||
112
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/os.go
generated
vendored
Normal file
112
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/os.go
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FakeOS mocks out certain OS calls to avoid perturbing the filesystem
|
||||
// If a member of the form `*Fn` is set, that function will be called in place
|
||||
// of the real call.
|
||||
type FakeOS struct {
|
||||
StatFn func(string) (os.FileInfo, error)
|
||||
ReadDirFn func(string) ([]os.FileInfo, error)
|
||||
MkdirAllFn func(string, os.FileMode) error
|
||||
SymlinkFn func(string, string) error
|
||||
HostName string
|
||||
Removes []string
|
||||
Files map[string][]*os.FileInfo
|
||||
}
|
||||
|
||||
func NewFakeOS() *FakeOS {
|
||||
return &FakeOS{
|
||||
Removes: []string{},
|
||||
Files: make(map[string][]*os.FileInfo),
|
||||
}
|
||||
}
|
||||
|
||||
// Mkdir is a fake call that just returns nil.
|
||||
func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
|
||||
if f.MkdirAllFn != nil {
|
||||
return f.MkdirAllFn(path, perm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Symlink is a fake call that just returns nil.
|
||||
func (f *FakeOS) Symlink(oldname string, newname string) error {
|
||||
if f.SymlinkFn != nil {
|
||||
return f.SymlinkFn(oldname, newname)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat is a fake that returns an error
|
||||
func (f FakeOS) Stat(path string) (os.FileInfo, error) {
|
||||
if f.StatFn != nil {
|
||||
return f.StatFn(path)
|
||||
}
|
||||
return nil, errors.New("unimplemented testing mock")
|
||||
}
|
||||
|
||||
// Remove is a fake call that returns nil.
|
||||
func (f *FakeOS) Remove(path string) error {
|
||||
f.Removes = append(f.Removes, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll is a fake call that just returns nil.
|
||||
func (f *FakeOS) RemoveAll(path string) error {
|
||||
f.Removes = append(f.Removes, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create is a fake call that returns nil.
|
||||
func (FakeOS) Create(path string) (*os.File, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Hostname is a fake call that returns nil.
|
||||
func (f *FakeOS) Hostname() (name string, err error) {
|
||||
return f.HostName, nil
|
||||
}
|
||||
|
||||
// Chtimes is a fake call that returns nil.
|
||||
func (FakeOS) Chtimes(path string, atime time.Time, mtime time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pipe is a fake call that returns nil.
|
||||
func (FakeOS) Pipe() (r *os.File, w *os.File, err error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// ReadDir is a fake call that returns the files under the directory.
|
||||
func (f *FakeOS) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
if f.ReadDirFn != nil {
|
||||
return f.ReadDirFn(dirname)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Glob is a fake call that returns nil.
|
||||
func (f *FakeOS) Glob(pattern string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
161
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/runtime_mock.go
generated
vendored
Normal file
161
vendor/k8s.io/kubernetes/pkg/kubelet/container/testing/runtime_mock.go
generated
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
. "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/util/term"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
type Mock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
var _ Runtime = new(Mock)
|
||||
|
||||
func (r *Mock) Start() error {
|
||||
args := r.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) Type() string {
|
||||
args := r.Called()
|
||||
return args.Get(0).(string)
|
||||
}
|
||||
|
||||
func (r *Mock) Version() (Version, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).(Version), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) APIVersion() (Version, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).(Version), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) Status() (*RuntimeStatus, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).(*RuntimeStatus), args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GetPods(all bool) ([]*Pod, error) {
|
||||
args := r.Called(all)
|
||||
return args.Get(0).([]*Pod), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) SyncPod(pod *v1.Pod, apiStatus v1.PodStatus, status *PodStatus, secrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult {
|
||||
args := r.Called(pod, apiStatus, status, secrets, backOff)
|
||||
return args.Get(0).(PodSyncResult)
|
||||
}
|
||||
|
||||
func (r *Mock) KillPod(pod *v1.Pod, runningPod Pod, gracePeriodOverride *int64) error {
|
||||
args := r.Called(pod, runningPod, gracePeriodOverride)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) RunContainerInPod(container v1.Container, pod *v1.Pod, volumeMap map[string]volume.VolumePlugin) error {
|
||||
args := r.Called(pod, pod, volumeMap)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) KillContainerInPod(container v1.Container, pod *v1.Pod) error {
|
||||
args := r.Called(pod, pod)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GetPodStatus(uid types.UID, name, namespace string) (*PodStatus, error) {
|
||||
args := r.Called(uid, name, namespace)
|
||||
return args.Get(0).(*PodStatus), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) ExecInContainer(containerID ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size, timeout time.Duration) error {
|
||||
args := r.Called(containerID, cmd, stdin, stdout, stderr, tty)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) AttachContainer(containerID ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) error {
|
||||
args := r.Called(containerID, stdin, stdout, stderr, tty)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GetContainerLogs(pod *v1.Pod, containerID ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) (err error) {
|
||||
args := r.Called(pod, containerID, logOptions, stdout, stderr)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) PullImage(image ImageSpec, pullSecrets []v1.Secret) error {
|
||||
args := r.Called(image, pullSecrets)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) IsImagePresent(image ImageSpec) (bool, error) {
|
||||
args := r.Called(image)
|
||||
return args.Get(0).(bool), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) ListImages() ([]Image, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).([]Image), args.Error(1)
|
||||
}
|
||||
|
||||
func (r *Mock) RemoveImage(image ImageSpec) error {
|
||||
args := r.Called(image)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) PortForward(pod *Pod, port uint16, stream io.ReadWriteCloser) error {
|
||||
args := r.Called(pod, port, stream)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GetNetNS(containerID ContainerID) (string, error) {
|
||||
args := r.Called(containerID)
|
||||
return "", args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GetPodContainerID(pod *Pod) (ContainerID, error) {
|
||||
args := r.Called(pod)
|
||||
return ContainerID{}, args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) GarbageCollect(gcPolicy ContainerGCPolicy, ready bool) error {
|
||||
args := r.Called(gcPolicy, ready)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) DeleteContainer(containerID ContainerID) error {
|
||||
args := r.Called(containerID)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) ImageStats() (*ImageStats, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).(*ImageStats), args.Error(1)
|
||||
}
|
||||
|
||||
// UpdatePodCIDR fulfills the cri interface.
|
||||
func (r *Mock) UpdatePodCIDR(c string) error {
|
||||
return nil
|
||||
}
|
||||
29
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/BUILD
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
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 = ["custom_metrics.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/api/v1:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["custom_metrics_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
],
|
||||
)
|
||||
48
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/custom_metrics.go
generated
vendored
Normal file
48
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/custom_metrics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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 custommetrics contains support for instrumenting cAdvisor to gather custom metrics from pods.
|
||||
package custommetrics
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
CustomMetricsDefinitionContainerFile = "definition.json"
|
||||
|
||||
CustomMetricsDefinitionDir = "/etc/custom-metrics"
|
||||
)
|
||||
|
||||
// Alpha implementation.
|
||||
// Returns a path to a cAdvisor-specific custom metrics configuration.
|
||||
func GetCAdvisorCustomMetricsDefinitionPath(container *v1.Container) (*string, error) {
|
||||
// Assuemes that the container has Custom Metrics enabled if it has "/etc/custom-metrics" directory
|
||||
// mounted as a volume. Custom Metrics definition is expected to be in "definition.json".
|
||||
if container.VolumeMounts != nil {
|
||||
for _, volumeMount := range container.VolumeMounts {
|
||||
if path.Clean(volumeMount.MountPath) == path.Clean(CustomMetricsDefinitionDir) {
|
||||
// TODO: add definition file validation.
|
||||
definitionPath := path.Clean(path.Join(volumeMount.MountPath, CustomMetricsDefinitionContainerFile))
|
||||
return &definitionPath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// No Custom Metrics definition available.
|
||||
return nil, nil
|
||||
}
|
||||
48
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/custom_metrics_test.go
generated
vendored
Normal file
48
vendor/k8s.io/kubernetes/pkg/kubelet/custommetrics/custom_metrics_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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 custommetrics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
func TestGetCAdvisorCustomMetricsDefinitionPath(t *testing.T) {
|
||||
|
||||
regularContainer := &v1.Container{
|
||||
Name: "test_container",
|
||||
}
|
||||
|
||||
cmContainer := &v1.Container{
|
||||
Name: "test_container",
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: "cm",
|
||||
MountPath: CustomMetricsDefinitionDir,
|
||||
},
|
||||
},
|
||||
}
|
||||
path, err := GetCAdvisorCustomMetricsDefinitionPath(regularContainer)
|
||||
assert.Nil(t, path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
path, err = GetCAdvisorCustomMetricsDefinitionPath(cmContainer)
|
||||
assert.NotEmpty(t, *path)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
137
vendor/k8s.io/kubernetes/pkg/kubelet/disk_manager.go
generated
vendored
Normal file
137
vendor/k8s.io/kubernetes/pkg/kubelet/disk_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
)
|
||||
|
||||
// Manages policy for diskspace management for disks holding docker images and root fs.
|
||||
|
||||
// mb is used to easily convert an int to an mb
|
||||
const mb = 1024 * 1024
|
||||
|
||||
// Implementation is thread-safe.
|
||||
type diskSpaceManager interface {
|
||||
// Checks the available disk space
|
||||
IsRootDiskSpaceAvailable() (bool, error)
|
||||
IsRuntimeDiskSpaceAvailable() (bool, error)
|
||||
}
|
||||
|
||||
type DiskSpacePolicy struct {
|
||||
// free disk space threshold for filesystem holding docker images.
|
||||
DockerFreeDiskMB int
|
||||
// free disk space threshold for root filesystem. Host volumes are created on root fs.
|
||||
RootFreeDiskMB int
|
||||
}
|
||||
|
||||
type fsInfo struct {
|
||||
Usage int64
|
||||
Capacity int64
|
||||
Available int64
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type realDiskSpaceManager struct {
|
||||
cadvisor cadvisor.Interface
|
||||
cachedInfo map[string]fsInfo // cache of filesystem info.
|
||||
lock sync.Mutex // protecting cachedInfo.
|
||||
policy DiskSpacePolicy // thresholds. Set at creation time.
|
||||
}
|
||||
|
||||
func (dm *realDiskSpaceManager) getFsInfo(fsType string, f func() (cadvisorapi.FsInfo, error)) (fsInfo, error) {
|
||||
dm.lock.Lock()
|
||||
defer dm.lock.Unlock()
|
||||
fsi := fsInfo{}
|
||||
if info, ok := dm.cachedInfo[fsType]; ok {
|
||||
timeLimit := time.Now().Add(-2 * time.Second)
|
||||
if info.Timestamp.After(timeLimit) {
|
||||
fsi = info
|
||||
}
|
||||
}
|
||||
if fsi.Timestamp.IsZero() {
|
||||
fs, err := f()
|
||||
if err != nil {
|
||||
return fsInfo{}, err
|
||||
}
|
||||
fsi.Timestamp = time.Now()
|
||||
fsi.Usage = int64(fs.Usage)
|
||||
fsi.Capacity = int64(fs.Capacity)
|
||||
fsi.Available = int64(fs.Available)
|
||||
dm.cachedInfo[fsType] = fsi
|
||||
}
|
||||
return fsi, nil
|
||||
}
|
||||
|
||||
func (dm *realDiskSpaceManager) IsRuntimeDiskSpaceAvailable() (bool, error) {
|
||||
return dm.isSpaceAvailable("runtime", dm.policy.DockerFreeDiskMB, dm.cadvisor.ImagesFsInfo)
|
||||
}
|
||||
|
||||
func (dm *realDiskSpaceManager) IsRootDiskSpaceAvailable() (bool, error) {
|
||||
return dm.isSpaceAvailable("root", dm.policy.RootFreeDiskMB, dm.cadvisor.RootFsInfo)
|
||||
}
|
||||
|
||||
func (dm *realDiskSpaceManager) isSpaceAvailable(fsType string, threshold int, f func() (cadvisorapi.FsInfo, error)) (bool, error) {
|
||||
fsInfo, err := dm.getFsInfo(fsType, f)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to get fs info for %q: %v", fsType, err)
|
||||
}
|
||||
if fsInfo.Capacity == 0 {
|
||||
return true, fmt.Errorf("could not determine capacity for %q fs. Info: %+v", fsType, fsInfo)
|
||||
}
|
||||
if fsInfo.Available < 0 {
|
||||
return true, fmt.Errorf("wrong available space for %q: %+v", fsType, fsInfo)
|
||||
}
|
||||
|
||||
if fsInfo.Available < int64(threshold)*mb {
|
||||
glog.Infof("Running out of space on disk for %q: available %d MB, threshold %d MB", fsType, fsInfo.Available/mb, threshold)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func validatePolicy(policy DiskSpacePolicy) error {
|
||||
if policy.DockerFreeDiskMB < 0 {
|
||||
return fmt.Errorf("free disk space should be non-negative. Invalid value %d for docker disk space threshold.", policy.DockerFreeDiskMB)
|
||||
}
|
||||
if policy.RootFreeDiskMB < 0 {
|
||||
return fmt.Errorf("free disk space should be non-negative. Invalid value %d for root disk space threshold.", policy.RootFreeDiskMB)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDiskSpaceManager(cadvisorInterface cadvisor.Interface, policy DiskSpacePolicy) (diskSpaceManager, error) {
|
||||
// validate policy
|
||||
err := validatePolicy(policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dm := &realDiskSpaceManager{
|
||||
cadvisor: cadvisorInterface,
|
||||
policy: policy,
|
||||
cachedInfo: map[string]fsInfo{},
|
||||
}
|
||||
|
||||
return dm, nil
|
||||
}
|
||||
295
vendor/k8s.io/kubernetes/pkg/kubelet/disk_manager_test.go
generated
vendored
Normal file
295
vendor/k8s.io/kubernetes/pkg/kubelet/disk_manager_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
|
||||
)
|
||||
|
||||
func testPolicy() DiskSpacePolicy {
|
||||
return DiskSpacePolicy{
|
||||
DockerFreeDiskMB: 250,
|
||||
RootFreeDiskMB: 250,
|
||||
}
|
||||
}
|
||||
|
||||
func setUp(t *testing.T) (*assert.Assertions, DiskSpacePolicy, *cadvisortest.Mock) {
|
||||
assert := assert.New(t)
|
||||
policy := testPolicy()
|
||||
c := new(cadvisortest.Mock)
|
||||
return assert, policy, c
|
||||
}
|
||||
|
||||
func TestValidPolicy(t *testing.T) {
|
||||
assert, policy, c := setUp(t)
|
||||
_, err := newDiskSpaceManager(c, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
policy = testPolicy()
|
||||
policy.DockerFreeDiskMB = -1
|
||||
_, err = newDiskSpaceManager(c, policy)
|
||||
assert.Error(err)
|
||||
|
||||
policy = testPolicy()
|
||||
policy.RootFreeDiskMB = -1
|
||||
_, err = newDiskSpaceManager(c, policy)
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
func TestSpaceAvailable(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
mockCadvisor.On("ImagesFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 400 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 600 * mb,
|
||||
}, nil)
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 9 * mb,
|
||||
Capacity: 10 * mb,
|
||||
}, nil)
|
||||
|
||||
ok, err := dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
|
||||
ok, err = dm.IsRootDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.False(ok)
|
||||
}
|
||||
|
||||
// TestIsRuntimeDiskSpaceAvailableWithSpace verifies IsRuntimeDiskSpaceAvailable results when
|
||||
// space is available.
|
||||
func TestIsRuntimeDiskSpaceAvailableWithSpace(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 500MB available
|
||||
mockCadvisor.On("ImagesFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 9500 * mb,
|
||||
Capacity: 10000 * mb,
|
||||
Available: 500 * mb,
|
||||
}, nil)
|
||||
|
||||
ok, err := dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
}
|
||||
|
||||
// TestIsRuntimeDiskSpaceAvailableWithoutSpace verifies IsRuntimeDiskSpaceAvailable results when
|
||||
// space is not available.
|
||||
func TestIsRuntimeDiskSpaceAvailableWithoutSpace(t *testing.T) {
|
||||
// 1MB available
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
mockCadvisor.On("ImagesFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 999 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 1 * mb,
|
||||
}, nil)
|
||||
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok, err := dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.False(ok)
|
||||
}
|
||||
|
||||
// TestIsRootDiskSpaceAvailableWithSpace verifies IsRootDiskSpaceAvailable results when
|
||||
// space is available.
|
||||
func TestIsRootDiskSpaceAvailableWithSpace(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
policy.RootFreeDiskMB = 10
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
// 999MB available
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 1 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 999 * mb,
|
||||
}, nil)
|
||||
|
||||
ok, err := dm.IsRootDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
}
|
||||
|
||||
// TestIsRootDiskSpaceAvailableWithoutSpace verifies IsRootDiskSpaceAvailable results when
|
||||
// space is not available.
|
||||
func TestIsRootDiskSpaceAvailableWithoutSpace(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
policy.RootFreeDiskMB = 10
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
// 9MB available
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 990 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 9 * mb,
|
||||
}, nil)
|
||||
|
||||
ok, err := dm.IsRootDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.False(ok)
|
||||
}
|
||||
|
||||
// TestCache verifies that caching works properly with DiskSpaceAvailable calls
|
||||
func TestCache(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
mockCadvisor.On("ImagesFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 400 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 300 * mb,
|
||||
}, nil).Once()
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 500 * mb,
|
||||
Capacity: 1000 * mb,
|
||||
Available: 500 * mb,
|
||||
}, nil).Once()
|
||||
|
||||
// Initial calls which should be recorded in mockCadvisor
|
||||
ok, err := dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
|
||||
ok, err = dm.IsRootDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
|
||||
// Get the current count of calls to mockCadvisor
|
||||
cadvisorCallCount := len(mockCadvisor.Calls)
|
||||
|
||||
// Checking for space again shouldn't need to mock as cache would serve it.
|
||||
ok, err = dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
|
||||
ok, err = dm.IsRootDiskSpaceAvailable()
|
||||
assert.NoError(err)
|
||||
assert.True(ok)
|
||||
|
||||
// Ensure no more calls to the mockCadvisor occurred
|
||||
assert.Equal(cadvisorCallCount, len(mockCadvisor.Calls))
|
||||
}
|
||||
|
||||
// TestFsInfoError verifies errors are returned by DiskSpaceAvailable calls
|
||||
// when FsInfo calls return an error
|
||||
func TestFsInfoError(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
policy.RootFreeDiskMB = 10
|
||||
dm, err := newDiskSpaceManager(mockCadvisor, policy)
|
||||
assert.NoError(err)
|
||||
|
||||
mockCadvisor.On("ImagesFsInfo").Return(cadvisorapi.FsInfo{}, fmt.Errorf("can't find fs"))
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{}, fmt.Errorf("EBUSY"))
|
||||
ok, err := dm.IsRuntimeDiskSpaceAvailable()
|
||||
assert.Error(err)
|
||||
assert.True(ok)
|
||||
ok, err = dm.IsRootDiskSpaceAvailable()
|
||||
assert.Error(err)
|
||||
assert.True(ok)
|
||||
}
|
||||
|
||||
// Test_getFSInfo verifies multiple possible cases for getFsInfo.
|
||||
func Test_getFsInfo(t *testing.T) {
|
||||
assert, policy, mockCadvisor := setUp(t)
|
||||
|
||||
// Sunny day case
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 10 * mb,
|
||||
Capacity: 100 * mb,
|
||||
Available: 90 * mb,
|
||||
}, nil).Once()
|
||||
|
||||
dm := &realDiskSpaceManager{
|
||||
cadvisor: mockCadvisor,
|
||||
policy: policy,
|
||||
cachedInfo: map[string]fsInfo{},
|
||||
}
|
||||
|
||||
available, err := dm.isSpaceAvailable("root", 10, dm.cadvisor.RootFsInfo)
|
||||
assert.True(available)
|
||||
assert.NoError(err)
|
||||
|
||||
// Threshold case
|
||||
mockCadvisor = new(cadvisortest.Mock)
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 9 * mb,
|
||||
Capacity: 100 * mb,
|
||||
Available: 9 * mb,
|
||||
}, nil).Once()
|
||||
|
||||
dm = &realDiskSpaceManager{
|
||||
cadvisor: mockCadvisor,
|
||||
policy: policy,
|
||||
cachedInfo: map[string]fsInfo{},
|
||||
}
|
||||
available, err = dm.isSpaceAvailable("root", 10, dm.cadvisor.RootFsInfo)
|
||||
assert.False(available)
|
||||
assert.NoError(err)
|
||||
|
||||
// Frozen case
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 9 * mb,
|
||||
Capacity: 10 * mb,
|
||||
Available: 500 * mb,
|
||||
}, nil).Once()
|
||||
|
||||
dm = &realDiskSpaceManager{
|
||||
cadvisor: mockCadvisor,
|
||||
policy: policy,
|
||||
cachedInfo: map[string]fsInfo{},
|
||||
}
|
||||
available, err = dm.isSpaceAvailable("root", 10, dm.cadvisor.RootFsInfo)
|
||||
assert.True(available)
|
||||
assert.NoError(err)
|
||||
|
||||
// Capacity error case
|
||||
mockCadvisor = new(cadvisortest.Mock)
|
||||
mockCadvisor.On("RootFsInfo").Return(cadvisorapi.FsInfo{
|
||||
Usage: 9 * mb,
|
||||
Capacity: 0,
|
||||
Available: 500 * mb,
|
||||
}, nil).Once()
|
||||
|
||||
dm = &realDiskSpaceManager{
|
||||
cadvisor: mockCadvisor,
|
||||
policy: policy,
|
||||
cachedInfo: map[string]fsInfo{},
|
||||
}
|
||||
available, err = dm.isSpaceAvailable("root", 10, dm.cadvisor.RootFsInfo)
|
||||
assert.True(available)
|
||||
assert.Error(err)
|
||||
assert.Contains(fmt.Sprintf("%s", err), "could not determine capacity")
|
||||
|
||||
// Available error case skipped as v2.FSInfo uses uint64 and this
|
||||
// can not be less than 0
|
||||
}
|
||||
19
vendor/k8s.io/kubernetes/pkg/kubelet/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/kubelet/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 kubelet is the package that contains the libraries that drive the Kubelet binary.
|
||||
// The kubelet is responsible for node level pod management. It runs on each worker in the cluster.
|
||||
package kubelet // import "k8s.io/kubernetes/pkg/kubelet"
|
||||
89
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/BUILD
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
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 = [
|
||||
"convert.go",
|
||||
"doc.go",
|
||||
"docker_container.go",
|
||||
"docker_image.go",
|
||||
"docker_sandbox.go",
|
||||
"docker_service.go",
|
||||
"docker_streaming.go",
|
||||
"helpers.go",
|
||||
"naming.go",
|
||||
"security_context.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/kubelet/api:go_default_library",
|
||||
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/cm:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/leaky:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/cni:go_default_library",
|
||||
"//pkg/kubelet/network/kubenet:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/kubelet/server/streaming:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/ioutils:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//vendor:github.com/docker/engine-api/types",
|
||||
"//vendor:github.com/docker/engine-api/types/container",
|
||||
"//vendor:github.com/docker/engine-api/types/filters",
|
||||
"//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/golang/protobuf/proto",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"convert_test.go",
|
||||
"docker_container_test.go",
|
||||
"docker_image_test.go",
|
||||
"docker_sandbox_test.go",
|
||||
"docker_service_test.go",
|
||||
"helpers_test.go",
|
||||
"naming_test.go",
|
||||
"security_context_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/kubelet/api/v1alpha1/runtime:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/mock_network:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//vendor:github.com/docker/engine-api/types",
|
||||
"//vendor:github.com/docker/engine-api/types/container",
|
||||
"//vendor:github.com/golang/mock/gomock",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
"//vendor:github.com/stretchr/testify/require",
|
||||
],
|
||||
)
|
||||
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/cm/BUILD
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/cm/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
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_manager.go",
|
||||
"container_manager_linux.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups/fs",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/configs",
|
||||
],
|
||||
)
|
||||
21
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/cm/container_manager.go
generated
vendored
Normal file
21
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/cm/container_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
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 cm
|
||||
|
||||
type ContainerManager interface {
|
||||
Start() error
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue