vendor update for CSI 0.3.0

This commit is contained in:
gman
2018-07-18 16:47:22 +02:00
parent 6f484f92fc
commit 8ea659f0d5
6810 changed files with 438061 additions and 193861 deletions

View File

@ -58,6 +58,7 @@ go_library(
"//pkg/apis/core/v1:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//pkg/kubelet/checkpoint:go_default_library",
"//pkg/kubelet/checkpointmanager:go_default_library",
"//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/events:go_default_library",
"//pkg/kubelet/types:go_default_library",
@ -80,6 +81,7 @@ go_library(
] + select({
"@io_bazel_rules_go//go/platform:linux": [
"//vendor/golang.org/x/exp/inotify:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
],
"//conditions:default": [],
}),
@ -91,6 +93,7 @@ go_test(
"apiserver_test.go",
"common_test.go",
"config_test.go",
"file_test.go",
"http_test.go",
] + select({
"@io_bazel_rules_go//go/platform:linux": [
@ -105,6 +108,8 @@ go_test(
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/v1:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//pkg/kubelet/checkpoint:go_default_library",
"//pkg/kubelet/checkpointmanager:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/securitycontext:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",

View File

@ -100,7 +100,7 @@ func getSelfLink(name, namespace string) string {
if len(namespace) == 0 {
namespace = metav1.NamespaceDefault
}
selfLink = fmt.Sprintf("/api/"+legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.Version+"/namespaces/%s/pods/%s", namespace, name)
selfLink = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", namespace, name)
return selfLink
}

View File

@ -78,7 +78,7 @@ func TestDecodeSinglePod(t *testing.T) {
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, podOut, string(json))
}
for _, gv := range legacyscheme.Registry.EnabledVersionsForGroup(v1.GroupName) {
for _, gv := range legacyscheme.Scheme.PrioritizedVersionsForGroup(v1.GroupName) {
info, _ := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), "application/yaml")
encoder := legacyscheme.Codecs.EncoderForVersion(info.Serializer, gv)
yaml, err := runtime.Encode(encoder, pod)
@ -144,7 +144,7 @@ func TestDecodePodList(t *testing.T) {
t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", podList, &podListOut, string(json))
}
for _, gv := range legacyscheme.Registry.EnabledVersionsForGroup(v1.GroupName) {
for _, gv := range legacyscheme.Scheme.PrioritizedVersionsForGroup(v1.GroupName) {
info, _ := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), "application/yaml")
encoder := legacyscheme.Codecs.EncoderForVersion(info.Serializer, gv)
yaml, err := runtime.Encode(encoder, podList)

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/kubelet/checkpoint"
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/events"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
@ -64,7 +65,7 @@ type PodConfig struct {
// contains the list of all configured sources
sourcesLock sync.Mutex
sources sets.String
checkpointManager checkpoint.Manager
checkpointManager checkpointmanager.CheckpointManager
}
// NewPodConfig creates an object that can merge many configuration sources into a stream
@ -112,15 +113,20 @@ func (c *PodConfig) Sync() {
// Restore restores pods from the checkpoint path, *once*
func (c *PodConfig) Restore(path string, updates chan<- interface{}) error {
var err error
if c.checkpointManager == nil {
c.checkpointManager = checkpoint.NewCheckpointManager(path)
pods, err := c.checkpointManager.LoadPods()
if err == nil {
updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.RESTORE, Source: kubetypes.ApiserverSource}
}
if c.checkpointManager != nil {
return nil
}
return err
var err error
c.checkpointManager, err = checkpointmanager.NewCheckpointManager(path)
if err != nil {
return err
}
pods, err := checkpoint.LoadPods(c.checkpointManager)
if err != nil {
return err
}
updates <- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.RESTORE, Source: kubetypes.ApiserverSource}
return nil
}
// podStorage manages the current pod state at any point in time and ensures updates
@ -308,6 +314,9 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
}
case kubetypes.RESTORE:
glog.V(4).Infof("Restoring pods for source %s", source)
for _, value := range update.Pods {
restorePods = append(restorePods, value)
}
default:
glog.Warningf("Received invalid update type: %v", update)

View File

@ -17,7 +17,9 @@ limitations under the License.
package config
import (
"io/ioutil"
"math/rand"
"os"
"reflect"
"sort"
"strconv"
@ -30,6 +32,9 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubelet/checkpoint"
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/securitycontext"
)
@ -85,6 +90,14 @@ func CreatePodUpdate(op kubetypes.PodOperation, source string, pods ...*v1.Pod)
return kubetypes.PodUpdate{Pods: pods, Op: op, Source: source}
}
func createPodConfigTesterByChannel(mode PodConfigNotificationMode, channelName string) (chan<- interface{}, <-chan kubetypes.PodUpdate, *PodConfig) {
eventBroadcaster := record.NewBroadcaster()
config := NewPodConfig(mode, eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kubelet"}))
channel := config.Channel(channelName)
ch := config.Updates()
return channel, ch, config
}
func createPodConfigTester(mode PodConfigNotificationMode) (chan<- interface{}, <-chan kubetypes.PodUpdate, *PodConfig) {
eventBroadcaster := record.NewBroadcaster()
config := NewPodConfig(mode, eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kubelet"}))
@ -413,3 +426,35 @@ func TestPodUpdateLabels(t *testing.T) {
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
}
func TestPodRestore(t *testing.T) {
tmpDir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(tmpDir)
pod := CreateValidPod("api-server", "kube-default")
pod.Annotations = make(map[string]string, 0)
pod.Annotations["kubernetes.io/config.source"] = kubetypes.ApiserverSource
pod.Annotations[core.BootstrapCheckpointAnnotationKey] = "true"
// Create Checkpointer
checkpointManager, err := checkpointmanager.NewCheckpointManager(tmpDir)
if err != nil {
t.Fatalf("failed to initialize checkpoint manager: %v", err)
}
if err := checkpoint.WritePod(checkpointManager, pod); err != nil {
t.Fatalf("Error writing checkpoint for pod: %v", pod.GetName())
}
// Restore checkpoint
channel, ch, config := createPodConfigTesterByChannel(PodConfigNotificationIncremental, kubetypes.ApiserverSource)
if err := config.Restore(tmpDir, channel); err != nil {
t.Fatalf("Restore returned error: %v", err)
}
expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.RESTORE, kubetypes.ApiserverSource, pod))
// Verify Restore only happen once
if err := config.Restore(tmpDir, channel); err != nil {
t.Fatalf("The second restore returned error: %v", err)
}
expectNoPodUpdate(t, ch)
}

View File

@ -30,30 +30,46 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
api "k8s.io/kubernetes/pkg/apis/core"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
)
type podEventType int
const (
podAdd podEventType = iota
podModify
podDelete
eventBufferLen = 10
)
type watchEvent struct {
fileName string
eventType podEventType
}
type sourceFile struct {
path string
nodeName types.NodeName
period time.Duration
store cache.Store
fileKeyMapping map[string]string
updates chan<- interface{}
watchEvents chan *watchEvent
}
func NewSourceFile(path string, nodeName types.NodeName, period time.Duration, updates chan<- interface{}) {
// "golang.org/x/exp/inotify" requires a path without trailing "/"
path = strings.TrimRight(path, string(os.PathSeparator))
config := newSourceFile(path, nodeName, updates)
config := newSourceFile(path, nodeName, period, updates)
glog.V(1).Infof("Watching path %q", path)
go wait.Forever(config.run, period)
config.run()
}
func newSourceFile(path string, nodeName types.NodeName, updates chan<- interface{}) *sourceFile {
func newSourceFile(path string, nodeName types.NodeName, period time.Duration, updates chan<- interface{}) *sourceFile {
send := func(objs []interface{}) {
var pods []*v1.Pod
for _, o := range objs {
@ -65,23 +81,40 @@ func newSourceFile(path string, nodeName types.NodeName, updates chan<- interfac
return &sourceFile{
path: path,
nodeName: nodeName,
period: period,
store: store,
fileKeyMapping: map[string]string{},
updates: updates,
watchEvents: make(chan *watchEvent, eventBufferLen),
}
}
func (s *sourceFile) run() {
if err := s.watch(); err != nil {
glog.Errorf("Unable to read manifest path %q: %v", s.path, err)
}
listTicker := time.NewTicker(s.period)
go func() {
for {
select {
case <-listTicker.C:
if err := s.listConfig(); err != nil {
glog.Errorf("Unable to read config path %q: %v", s.path, err)
}
case e := <-s.watchEvents:
if err := s.consumeWatchEvent(e); err != nil {
glog.Errorf("Unable to process watch event: %v", err)
}
}
}
}()
s.startWatch()
}
func (s *sourceFile) applyDefaults(pod *api.Pod, source string) error {
return applyDefaults(pod, source, true, s.nodeName)
}
func (s *sourceFile) resetStoreFromPath() error {
func (s *sourceFile) listConfig() error {
path := s.path
statInfo, err := os.Stat(path)
if err != nil {
@ -158,7 +191,7 @@ func (s *sourceFile) extractFromDir(name string) ([]*v1.Pod, error) {
}
func (s *sourceFile) extractFromFile(filename string) (pod *v1.Pod, err error) {
glog.V(3).Infof("Reading manifest file %q", filename)
glog.V(3).Infof("Reading config file %q", filename)
defer func() {
if err == nil && pod != nil {
objKey, keyErr := cache.MetaNamespaceKeyFunc(pod)
@ -193,7 +226,7 @@ func (s *sourceFile) extractFromFile(filename string) (pod *v1.Pod, err error) {
return pod, nil
}
return pod, fmt.Errorf("%v: couldn't parse as pod(%v), please check manifest file.\n", filename, podErr)
return pod, fmt.Errorf("%v: couldn't parse as pod(%v), please check config file.\n", filename, podErr)
}
func (s *sourceFile) replaceStore(pods ...*v1.Pod) (err error) {

View File

@ -24,23 +24,49 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/golang/glog"
"golang.org/x/exp/inotify"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/flowcontrol"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
)
type podEventType int
const (
podAdd podEventType = iota
podModify
podDelete
retryPeriod = 1 * time.Second
maxRetryPeriod = 20 * time.Second
)
func (s *sourceFile) watch() error {
type retryableError struct {
message string
}
func (e *retryableError) Error() string {
return e.message
}
func (s *sourceFile) startWatch() {
backOff := flowcontrol.NewBackOff(retryPeriod, maxRetryPeriod)
backOffId := "watch"
go wait.Forever(func() {
if backOff.IsInBackOffSinceUpdate(backOffId, time.Now()) {
return
}
if err := s.doWatch(); err != nil {
glog.Errorf("Unable to read config path %q: %v", s.path, err)
if _, retryable := err.(*retryableError); !retryable {
backOff.Next(backOffId, time.Now())
}
}
}, retryPeriod)
}
func (s *sourceFile) doWatch() error {
_, err := os.Stat(s.path)
if err != nil {
if !os.IsNotExist(err) {
@ -48,7 +74,7 @@ func (s *sourceFile) watch() error {
}
// 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")
return &retryableError{"path does not exist, ignoring"}
}
w, err := inotify.NewWatcher()
@ -57,22 +83,16 @@ func (s *sourceFile) watch() error {
}
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)
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|inotify.IN_ATTRIB)
if err != nil {
return fmt.Errorf("unable to create inotify for path %q: %v", s.path, err)
}
// Reset store with manifest files already existing when starting
if err := s.resetStoreFromPath(); err != nil {
return fmt.Errorf("unable to read manifest 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)
if err = s.produceWatchEvent(event); err != nil {
return fmt.Errorf("error while processing inotify event (%+v): %v", event, err)
}
case err = <-w.Error:
return fmt.Errorf("error while watching %q: %v", s.path, err)
@ -80,7 +100,7 @@ func (s *sourceFile) watch() error {
}
}
func (s *sourceFile) processEvent(e *inotify.Event) error {
func (s *sourceFile) produceWatchEvent(e *inotify.Event) error {
// Ignore file start with dots
if strings.HasPrefix(filepath.Base(e.Name), ".") {
glog.V(4).Infof("Ignored pod manifest: %s, because it starts with dots", e.Name)
@ -97,6 +117,8 @@ func (s *sourceFile) processEvent(e *inotify.Event) error {
eventType = podAdd
case (e.Mask & inotify.IN_MODIFY) > 0:
eventType = podModify
case (e.Mask & inotify.IN_ATTRIB) > 0:
eventType = podModify
case (e.Mask & inotify.IN_DELETE) > 0:
eventType = podDelete
case (e.Mask & inotify.IN_MOVED_FROM) > 0:
@ -108,22 +130,31 @@ func (s *sourceFile) processEvent(e *inotify.Event) error {
return nil
}
switch eventType {
s.watchEvents <- &watchEvent{e.Name, eventType}
return nil
}
func (s *sourceFile) consumeWatchEvent(e *watchEvent) error {
switch e.eventType {
case podAdd, podModify:
if pod, err := s.extractFromFile(e.Name); err != nil {
glog.Errorf("Can't process manifest file %q: %v", e.Name, err)
if pod, err := s.extractFromFile(e.fileName); err != nil {
return fmt.Errorf("can't process config file %q: %v", e.fileName, err)
} else {
return s.store.Add(pod)
}
case podDelete:
if objKey, keyExist := s.fileKeyMapping[e.Name]; keyExist {
if objKey, keyExist := s.fileKeyMapping[e.fileName]; 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)
if err = s.store.Delete(pod); err != nil {
return fmt.Errorf("failed to remove deleted pod from cache: %v", err)
} else {
delete(s.fileKeyMapping, e.fileName)
}
}
}
}

View File

@ -21,7 +21,6 @@ package config
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@ -35,7 +34,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
api "k8s.io/kubernetes/pkg/apis/core"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
@ -46,8 +45,8 @@ import (
func TestExtractFromNonExistentFile(t *testing.T) {
ch := make(chan interface{}, 1)
c := newSourceFile("/some/fake/file", "localhost", ch)
err := c.watch()
lw := newSourceFile("/some/fake/file", "localhost", time.Millisecond, ch)
err := lw.doWatch()
if err == nil {
t.Errorf("Expected error")
}
@ -75,7 +74,7 @@ func TestReadPodsFromFileExistAlready(t *testing.T) {
for _, testCase := range testCases {
func() {
dirName, err := utiltesting.MkTmpdir("file-test")
dirName, err := mkTempDir("file-test")
if err != nil {
t.Fatalf("unable to create temp dir: %v", err)
}
@ -107,69 +106,35 @@ func TestReadPodsFromFileExistAlready(t *testing.T) {
}
}
func TestReadPodsFromFileExistLater(t *testing.T) {
watchFileAdded(false, t)
var (
testCases = []struct {
watchDir bool
symlink bool
}{
{true, true},
{true, false},
{false, true},
{false, false},
}
)
func TestWatchFileAdded(t *testing.T) {
for _, testCase := range testCases {
watchFileAdded(testCase.watchDir, testCase.symlink, 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_manifest")
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 := newSourceFile(fileName, "localhost", 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 := newSourceFile(dirName, "localhost", ch)
err = c.resetStoreFromPath()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := (<-ch).(kubetypes.PodUpdate)
expected := CreatePodUpdate(kubetypes.SET, kubetypes.FileSource)
if !apiequality.Semantic.DeepEqual(expected, update) {
t.Fatalf("expected %#v, Got %#v", expected, update)
func TestWatchFileChanged(t *testing.T) {
for _, testCase := range testCases {
watchFileChanged(testCase.watchDir, testCase.symlink, t)
}
}
type testCase struct {
desc string
pod runtime.Object
expected kubetypes.PodUpdate
desc string
linkedFile string
pod runtime.Object
expected kubetypes.PodUpdate
}
func getTestCases(hostname types.NodeName) []*testCase {
@ -234,7 +199,7 @@ func getTestCases(hostname types.NodeName) []*testCase {
func (tc *testCase) writeToFile(dir, name string, t *testing.T) string {
var versionedPod runtime.Object
err := testapi.Default.Converter().Convert(&tc.pod, &versionedPod, nil)
err := legacyscheme.Scheme.Convert(&tc.pod, &versionedPod, nil)
if err != nil {
t.Fatalf("%s: error in versioning the pod: %v", tc.desc, err)
}
@ -250,19 +215,40 @@ func (tc *testCase) writeToFile(dir, name string, t *testing.T) string {
return fileName
}
func watchFileAdded(watchDir bool, t *testing.T) {
func createSymbolicLink(link, target, name string, t *testing.T) string {
linkName := filepath.Join(link, name)
linkedFile := filepath.Join(target, name)
err := os.Symlink(linkedFile, linkName)
if err != nil {
t.Fatalf("unexpected error when create symbolic link: %v", err)
}
return linkName
}
func watchFileAdded(watchDir bool, symlink bool, t *testing.T) {
hostname := types.NodeName("random-test-hostname")
var testCases = getTestCases(hostname)
fileNamePre := "test_pod_manifest"
for index, testCase := range testCases {
func() {
dirName, err := utiltesting.MkTmpdir("dir-test")
dirName, err := mkTempDir("dir-test")
if err != nil {
t.Fatalf("unable to create temp dir: %v", err)
}
defer os.RemoveAll(dirName)
defer removeAll(dirName, t)
fileName := fmt.Sprintf("%s_%d", fileNamePre, index)
var linkedDirName string
if symlink {
linkedDirName, err = mkTempDir("linked-dir-test")
if err != nil {
t.Fatalf("unable to create temp dir for linked files: %v", err)
}
defer removeAll(linkedDirName, t)
createSymbolicLink(dirName, linkedDirName, fileName, t)
}
ch := make(chan interface{})
if watchDir {
@ -274,12 +260,17 @@ func watchFileAdded(watchDir bool, t *testing.T) {
addFile := func() {
// Add a file
if symlink {
testCase.writeToFile(linkedDirName, fileName, t)
return
}
testCase.writeToFile(dirName, fileName, t)
}
go addFile()
// For !watchDir: expect an update by SourceFile.resetStoreFromPath().
// For !watchDir: expect an update by SourceFile.reloadConfig().
// 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.
@ -288,19 +279,29 @@ func watchFileAdded(watchDir bool, t *testing.T) {
}
}
func watchFileChanged(watchDir bool, t *testing.T) {
func watchFileChanged(watchDir bool, symlink bool, t *testing.T) {
hostname := types.NodeName("random-test-hostname")
var testCases = getTestCases(hostname)
fileNamePre := "test_pod_manifest"
for index, testCase := range testCases {
func() {
dirName, err := utiltesting.MkTmpdir("dir-test")
dirName, err := mkTempDir("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)
defer removeAll(dirName, t)
var linkedDirName string
if symlink {
linkedDirName, err = mkTempDir("linked-dir-test")
if err != nil {
t.Fatalf("unable to create temp dir for linked files: %v", err)
}
defer removeAll(linkedDirName, t)
createSymbolicLink(dirName, linkedDirName, fileName, t)
}
var file string
lock := &sync.Mutex{}
@ -308,6 +309,12 @@ func watchFileChanged(watchDir bool, t *testing.T) {
func() {
lock.Lock()
defer lock.Unlock()
if symlink {
file = testCase.writeToFile(linkedDirName, fileName, t)
return
}
file = testCase.writeToFile(dirName, fileName, t)
}()
@ -332,7 +339,12 @@ func watchFileChanged(watchDir bool, t *testing.T) {
pod.Spec.Containers[0].Name = "image2"
testCase.expected.Pods[0].Spec.Containers[0].Name = "image2"
testCase.writeToFile(dirName, fileName, t)
if symlink {
file = testCase.writeToFile(linkedDirName, fileName, t)
return
}
file = testCase.writeToFile(dirName, fileName, t)
}
go changeFile()
@ -370,6 +382,10 @@ func expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) {
select {
case got := <-ch:
update := got.(kubetypes.PodUpdate)
if len(update.Pods) == 0 {
// filter out the empty updates from reading a non-existing path
continue
}
for _, pod := range update.Pods {
// TODO: remove the conversion when validation is performed on versioned objects.
internalPod := &api.Pod{}

View File

@ -0,0 +1,84 @@
/*
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 (
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
apiequality "k8s.io/apimachinery/pkg/api/equality"
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
)
func TestExtractFromBadDataFile(t *testing.T) {
dirName, err := mkTempDir("file-test")
if err != nil {
t.Fatalf("unable to create temp dir: %v", err)
}
defer removeAll(dirName, t)
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)
lw := newSourceFile(fileName, "localhost", time.Millisecond, ch)
err = lw.listConfig()
if err == nil {
t.Fatalf("expected error, got nil")
}
expectEmptyChannel(t, ch)
}
func TestExtractFromEmptyDir(t *testing.T) {
dirName, err := mkTempDir("file-test")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer removeAll(dirName, t)
ch := make(chan interface{}, 1)
lw := newSourceFile(dirName, "localhost", time.Millisecond, ch)
err = lw.listConfig()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
update, ok := (<-ch).(kubetypes.PodUpdate)
if !ok {
t.Fatalf("unexpected type: %#v", update)
}
expected := CreatePodUpdate(kubetypes.SET, kubetypes.FileSource)
if !apiequality.Semantic.DeepEqual(expected, update) {
t.Fatalf("expected %#v, got %#v", expected, update)
}
}
func mkTempDir(prefix string) (string, error) {
return ioutil.TempDir(os.TempDir(), prefix)
}
func removeAll(dir string, t *testing.T) {
if err := os.RemoveAll(dir); err != nil {
t.Fatalf("unable to remove dir %s: %v", dir, err)
}
}

View File

@ -19,8 +19,16 @@ limitations under the License.
// Reads the pod configuration from file or a directory of files.
package config
import "errors"
import (
"fmt"
func (s *sourceFile) watch() error {
return errors.New("source file is unsupported in this build")
"github.com/golang/glog"
)
func (s *sourceFile) startWatch() {
glog.Errorf("Watching source file is unsupported in this build")
}
func (s *sourceFile) consumeWatchEvent(e *watchEvent) error {
return fmt.Errorf("consuming watch event is unsupported in this build")
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package config
import (
"fmt"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -29,6 +31,15 @@ type ContainerRuntimeOptions struct {
ContainerRuntime string
// RuntimeCgroups that container runtime is expected to be isolated in.
RuntimeCgroups string
// RedirectContainerStreaming enables container streaming redirect.
// When RedirectContainerStreaming is false, kubelet will proxy container streaming data
// between apiserver and container runtime. This approach is more secure, but the proxy
// introduces some overhead.
// When RedirectContainerStreaming is true, kubelet will return an http redirect to apiserver,
// and apiserver will access container runtime directly. This approach is more performant,
// but less secure because the connection between apiserver and container runtime is not
// authenticated.
RedirectContainerStreaming bool
// Docker-specific options.
@ -67,45 +78,30 @@ type ContainerRuntimeOptions struct {
// CNIBinDir is the full path of the directory in which to search for
// CNI plugin binaries
CNIBinDir string
// rkt-specific options.
// rktPath is the path of rkt binary. Leave empty to use the first rkt in $PATH.
RktPath string
// rktApiEndpoint is the endpoint of the rkt API service to communicate with.
RktAPIEndpoint string
// rktStage1Image is the image to use as stage1. Local paths and
// http/https URLs are supported.
RktStage1Image string
}
func (s *ContainerRuntimeOptions) AddFlags(fs *pflag.FlagSet) {
dockerOnlyWarning := "This docker-specific flag only works when container-runtime is set to docker."
// General settings.
fs.StringVar(&s.ContainerRuntime, "container-runtime", s.ContainerRuntime, "The container runtime to use. Possible values: 'docker', 'rkt'.")
fs.StringVar(&s.ContainerRuntime, "container-runtime", s.ContainerRuntime, "The container runtime to use. Possible values: 'docker', 'remote', 'rkt (deprecated)'.")
fs.StringVar(&s.RuntimeCgroups, "runtime-cgroups", s.RuntimeCgroups, "Optional absolute name of cgroups to create and run the runtime in.")
fs.BoolVar(&s.RedirectContainerStreaming, "redirect-container-streaming", s.RedirectContainerStreaming, "Enables container streaming redirect. If false, kubelet will proxy container streaming data between apiserver and container runtime; if true, kubelet will return an http redirect to apiserver, and apiserver will access container runtime directly. The proxy approach is more secure, but introduces some overhead. The redirect approach is more performant, but less secure because the connection between apiserver and container runtime may not be authenticated.")
// Docker-specific settings.
fs.BoolVar(&s.ExperimentalDockershim, "experimental-dockershim", s.ExperimentalDockershim, "Enable dockershim only mode. In this mode, kubelet will only start dockershim without any other functionalities. This flag only serves test purpose, please do not use it unless you are conscious of what you are doing. [default=false]")
fs.MarkHidden("experimental-dockershim")
fs.StringVar(&s.DockershimRootDirectory, "experimental-dockershim-root-directory", s.DockershimRootDirectory, "Path to the dockershim root directory.")
fs.MarkHidden("experimental-dockershim-root-directory")
fs.BoolVar(&s.DockerDisableSharedPID, "docker-disable-shared-pid", s.DockerDisableSharedPID, "Setting this to false causes Kubernetes to create pods using a shared process namespace for containers in a pod when running with Docker 1.13.1 or higher. A future Kubernetes release will make this configurable instead in the API.")
fs.BoolVar(&s.DockerDisableSharedPID, "docker-disable-shared-pid", s.DockerDisableSharedPID, fmt.Sprintf("Setting this to false causes Kubernetes to create pods using a shared process namespace for containers in a pod when running with Docker 1.13.1 or higher. A future Kubernetes release will make this configurable instead in the API. %s", dockerOnlyWarning))
fs.MarkDeprecated("docker-disable-shared-pid", "will be removed in a future release. This option will be replaced by PID namespace sharing that is configurable per-pod using the API. See https://features.k8s.io/495")
fs.StringVar(&s.PodSandboxImage, "pod-infra-container-image", s.PodSandboxImage, "The image whose network/ipc namespaces containers in each pod will use.")
fs.StringVar(&s.DockerEndpoint, "docker-endpoint", s.DockerEndpoint, "Use this for the docker endpoint to communicate with")
fs.DurationVar(&s.ImagePullProgressDeadline.Duration, "image-pull-progress-deadline", s.ImagePullProgressDeadline.Duration, "If no pulling progress is made before this deadline, the image pulling will be cancelled.")
fs.StringVar(&s.PodSandboxImage, "pod-infra-container-image", s.PodSandboxImage, fmt.Sprintf("The image whose network/ipc namespaces containers in each pod will use. %s", dockerOnlyWarning))
fs.StringVar(&s.DockerEndpoint, "docker-endpoint", s.DockerEndpoint, fmt.Sprintf("Use this for the docker endpoint to communicate with %s", dockerOnlyWarning))
fs.DurationVar(&s.ImagePullProgressDeadline.Duration, "image-pull-progress-deadline", s.ImagePullProgressDeadline.Duration, fmt.Sprintf("If no pulling progress is made before this deadline, the image pulling will be cancelled. %s", dockerOnlyWarning))
// Network plugin settings. Shared by both docker and rkt.
fs.StringVar(&s.NetworkPluginName, "network-plugin", s.NetworkPluginName, "<Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle")
fs.StringVar(&s.CNIConfDir, "cni-conf-dir", s.CNIConfDir, "<Warning: Alpha feature> The full path of the directory in which to search for CNI config files. Default: /etc/cni/net.d")
fs.StringVar(&s.CNIBinDir, "cni-bin-dir", s.CNIBinDir, "<Warning: Alpha feature> The full path of the directory in which to search for CNI plugin binaries. Default: /opt/cni/bin")
fs.Int32Var(&s.NetworkPluginMTU, "network-plugin-mtu", s.NetworkPluginMTU, "<Warning: Alpha feature> The MTU to be passed to the network plugin, to override the default. Set to 0 to use the default 1460 MTU.")
// Rkt-specific settings.
fs.StringVar(&s.RktPath, "rkt-path", s.RktPath, "Path of rkt binary. Leave empty to use the first rkt in $PATH. Only used if --container-runtime='rkt'.")
fs.MarkDeprecated("rkt-path", "will be removed in a future version. Rktnetes has been deprecated in favor of rktlet (https://github.com/kubernetes-incubator/rktlet).")
fs.StringVar(&s.RktAPIEndpoint, "rkt-api-endpoint", s.RktAPIEndpoint, "The endpoint of the rkt API service to communicate with. Only used if --container-runtime='rkt'.")
fs.MarkDeprecated("rkt-api-endpoint", "will be removed in a future version. Rktnetes has been deprecated in favor of rktlet (https://github.com/kubernetes-incubator/rktlet).")
fs.StringVar(&s.RktStage1Image, "rkt-stage1-image", s.RktStage1Image, "image to use as stage1. Local paths and http/https URLs are supported. If empty, the 'stage1.aci' in the same directory as '--rkt-path' will be used.")
fs.MarkDeprecated("rkt-stage1-image", "will be removed in a future version. Rktnetes has been deprecated in favor of rktlet (https://github.com/kubernetes-incubator/rktlet).")
// Network plugin settings for Docker.
fs.StringVar(&s.NetworkPluginName, "network-plugin", s.NetworkPluginName, fmt.Sprintf("<Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle. %s", dockerOnlyWarning))
fs.StringVar(&s.CNIConfDir, "cni-conf-dir", s.CNIConfDir, fmt.Sprintf("<Warning: Alpha feature> The full path of the directory in which to search for CNI config files. Default: /etc/cni/net.d. %s", dockerOnlyWarning))
fs.StringVar(&s.CNIBinDir, "cni-bin-dir", s.CNIBinDir, fmt.Sprintf("<Warning: Alpha feature> A comma-separated list of full paths of directories in which to search for CNI plugin binaries. Default: /opt/cni/bin. %s", dockerOnlyWarning))
fs.Int32Var(&s.NetworkPluginMTU, "network-plugin-mtu", s.NetworkPluginMTU, fmt.Sprintf("<Warning: Alpha feature> The MTU to be passed to the network plugin, to override the default. Set to 0 to use the default 1460 MTU. %s", dockerOnlyWarning))
}

View File

@ -72,7 +72,7 @@ func TestExtractInvalidPods(t *testing.T) {
{
desc: "Invalid volume name",
pod: &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
Spec: v1.PodSpec{
Volumes: []v1.Volume{{Name: "_INVALID_"}},
},
@ -81,7 +81,7 @@ func TestExtractInvalidPods(t *testing.T) {
{
desc: "Duplicate volume names",
pod: &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
Spec: v1.PodSpec{
Volumes: []v1.Volume{{Name: "repeated"}, {Name: "repeated"}},
},
@ -90,7 +90,7 @@ func TestExtractInvalidPods(t *testing.T) {
{
desc: "Unspecified container name",
pod: &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: ""}},
},
@ -99,7 +99,7 @@ func TestExtractInvalidPods(t *testing.T) {
{
desc: "Invalid container name",
pod: &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "_INVALID_"}},
},
@ -290,7 +290,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
for _, testCase := range testCases {
var versionedPods runtime.Object
err := testapi.Default.Converter().Convert(&testCase.pods, &versionedPods, nil)
err := legacyscheme.Scheme.Convert(&testCase.pods, &versionedPods, nil)
if err != nil {
t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err)
}
@ -331,7 +331,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
func TestURLWithHeader(t *testing.T) {
pod := &v1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
APIVersion: "v1",
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{