2016-09-23 01:51:12 +00:00
|
|
|
// +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"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2017-06-22 17:25:57 +00:00
|
|
|
"k8s.io/api/core/v1"
|
2017-01-25 13:39:54 +00:00
|
|
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
2017-01-11 14:09:48 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
2018-04-25 14:55:17 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
2016-09-23 01:51:12 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/testapi"
|
2017-11-08 22:34:54 +00:00
|
|
|
api "k8s.io/kubernetes/pkg/apis/core"
|
|
|
|
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
|
|
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
2016-09-23 01:51:12 +00:00
|
|
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
|
|
|
"k8s.io/kubernetes/pkg/securitycontext"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestExtractFromNonExistentFile(t *testing.T) {
|
|
|
|
ch := make(chan interface{}, 1)
|
2018-01-09 05:39:24 +00:00
|
|
|
lw := newSourceFile("/some/fake/file", "localhost", time.Millisecond, ch)
|
|
|
|
err := lw.doWatch()
|
2016-09-23 01:51:12 +00:00
|
|
|
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)
|
2017-01-25 13:39:54 +00:00
|
|
|
if !apiequality.Semantic.DeepDerivative(expected, update) {
|
2016-09-23 01:51:12 +00:00
|
|
|
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() {
|
2018-01-09 05:39:24 +00:00
|
|
|
dirName, err := mkTempDir("file-test")
|
2016-09-23 01:51:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create temp dir: %v", err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(dirName)
|
2017-06-16 08:43:52 +00:00
|
|
|
file := testCase.writeToFile(dirName, "test_pod_manifest", t)
|
2016-09-23 01:51:12 +00:00
|
|
|
|
|
|
|
ch := make(chan interface{})
|
|
|
|
NewSourceFile(file, hostname, time.Millisecond, ch)
|
|
|
|
select {
|
|
|
|
case got := <-ch:
|
|
|
|
update := got.(kubetypes.PodUpdate)
|
|
|
|
for _, pod := range update.Pods {
|
2016-11-18 20:50:58 +00:00
|
|
|
// TODO: remove the conversion when validation is performed on versioned objects.
|
2016-11-19 23:32:10 +00:00
|
|
|
internalPod := &api.Pod{}
|
2017-10-09 17:13:46 +00:00
|
|
|
if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
|
2016-11-19 23:32:10 +00:00
|
|
|
t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
|
|
|
|
}
|
|
|
|
if errs := validation.ValidatePod(internalPod); len(errs) > 0 {
|
2016-11-18 20:50:58 +00:00
|
|
|
t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-25 13:39:54 +00:00
|
|
|
if !apiequality.Semantic.DeepEqual(testCase.expected, update) {
|
2016-09-23 01:51:12 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
var (
|
|
|
|
testCases = []struct {
|
|
|
|
watchDir bool
|
|
|
|
symlink bool
|
|
|
|
}{
|
|
|
|
{true, true},
|
|
|
|
{true, false},
|
|
|
|
{false, true},
|
|
|
|
{false, false},
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
2018-01-09 05:39:24 +00:00
|
|
|
)
|
2016-09-23 01:51:12 +00:00
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
func TestWatchFileAdded(t *testing.T) {
|
|
|
|
for _, testCase := range testCases {
|
|
|
|
watchFileAdded(testCase.watchDir, testCase.symlink, t)
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
func TestWatchFileChanged(t *testing.T) {
|
|
|
|
for _, testCase := range testCases {
|
|
|
|
watchFileChanged(testCase.watchDir, testCase.symlink, t)
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type testCase struct {
|
2018-06-04 03:58:43 +00:00
|
|
|
lock *sync.Mutex
|
2018-01-09 05:39:24 +00:00
|
|
|
desc string
|
|
|
|
linkedFile string
|
|
|
|
pod runtime.Object
|
|
|
|
expected kubetypes.PodUpdate
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getTestCases(hostname types.NodeName) []*testCase {
|
|
|
|
grace := int64(30)
|
2018-09-22 00:06:32 +00:00
|
|
|
enableServiceLinks := v1.DefaultEnableServiceLinks
|
2016-09-23 01:51:12 +00:00
|
|
|
return []*testCase{
|
|
|
|
{
|
2018-06-04 03:58:43 +00:00
|
|
|
lock: &sync.Mutex{},
|
2016-09-23 01:51:12 +00:00
|
|
|
desc: "Simple pod",
|
2016-11-18 20:50:58 +00:00
|
|
|
pod: &v1.Pod{
|
2016-12-03 18:57:26 +00:00
|
|
|
TypeMeta: metav1.TypeMeta{
|
2016-09-23 01:51:12 +00:00
|
|
|
Kind: "Pod",
|
|
|
|
APIVersion: "",
|
|
|
|
},
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-09-23 01:51:12 +00:00
|
|
|
Name: "test",
|
|
|
|
UID: "12345",
|
|
|
|
Namespace: "mynamespace",
|
|
|
|
},
|
2016-11-18 20:50:58 +00:00
|
|
|
Spec: v1.PodSpec{
|
|
|
|
Containers: []v1.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
|
|
SecurityContext: &v1.PodSecurityContext{},
|
2017-01-05 23:47:54 +00:00
|
|
|
SchedulerName: api.DefaultSchedulerName,
|
2016-09-23 01:51:12 +00:00
|
|
|
},
|
2016-11-18 20:50:58 +00:00
|
|
|
Status: v1.PodStatus{
|
|
|
|
Phase: v1.PodPending,
|
2016-09-23 01:51:12 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:58 +00:00
|
|
|
expected: CreatePodUpdate(kubetypes.SET, kubetypes.FileSource, &v1.Pod{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-09-23 01:51:12 +00:00
|
|
|
Name: "test-" + string(hostname),
|
|
|
|
UID: "12345",
|
|
|
|
Namespace: "mynamespace",
|
|
|
|
Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "12345"},
|
|
|
|
SelfLink: getSelfLink("test-"+string(hostname), "mynamespace"),
|
|
|
|
},
|
2016-11-18 20:50:58 +00:00
|
|
|
Spec: v1.PodSpec{
|
2016-09-23 01:51:12 +00:00
|
|
|
NodeName: string(hostname),
|
2016-11-18 20:50:58 +00:00
|
|
|
RestartPolicy: v1.RestartPolicyAlways,
|
|
|
|
DNSPolicy: v1.DNSClusterFirst,
|
2016-09-23 01:51:12 +00:00
|
|
|
TerminationGracePeriodSeconds: &grace,
|
2017-03-17 21:06:34 +00:00
|
|
|
Tolerations: []v1.Toleration{{
|
|
|
|
Operator: "Exists",
|
|
|
|
Effect: "NoExecute",
|
|
|
|
}},
|
2016-11-18 20:50:58 +00:00
|
|
|
Containers: []v1.Container{{
|
2018-10-05 19:59:38 +00:00
|
|
|
Name: "image",
|
|
|
|
Image: "test/image",
|
2017-01-16 04:56:22 +00:00
|
|
|
TerminationMessagePath: "/dev/termination-log",
|
|
|
|
ImagePullPolicy: "Always",
|
|
|
|
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
|
|
TerminationMessagePolicy: v1.TerminationMessageReadFile,
|
|
|
|
}},
|
2018-09-17 20:27:36 +00:00
|
|
|
SecurityContext: &v1.PodSecurityContext{},
|
|
|
|
SchedulerName: api.DefaultSchedulerName,
|
|
|
|
EnableServiceLinks: &enableServiceLinks,
|
2016-09-23 01:51:12 +00:00
|
|
|
},
|
2016-11-18 20:50:58 +00:00
|
|
|
Status: v1.PodStatus{
|
|
|
|
Phase: v1.PodPending,
|
2016-09-23 01:51:12 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tc *testCase) writeToFile(dir, name string, t *testing.T) string {
|
|
|
|
var versionedPod runtime.Object
|
2018-04-25 14:55:17 +00:00
|
|
|
err := legacyscheme.Scheme.Convert(&tc.pod, &versionedPod, nil)
|
2016-09-23 01:51:12 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
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) {
|
2016-09-23 01:51:12 +00:00
|
|
|
hostname := types.NodeName("random-test-hostname")
|
|
|
|
var testCases = getTestCases(hostname)
|
|
|
|
|
2017-06-16 08:43:52 +00:00
|
|
|
fileNamePre := "test_pod_manifest"
|
2016-09-23 01:51:12 +00:00
|
|
|
for index, testCase := range testCases {
|
|
|
|
func() {
|
2018-01-09 05:39:24 +00:00
|
|
|
dirName, err := mkTempDir("dir-test")
|
2016-09-23 01:51:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create temp dir: %v", err)
|
|
|
|
}
|
2018-01-09 05:39:24 +00:00
|
|
|
defer removeAll(dirName, t)
|
|
|
|
|
2016-09-23 01:51:12 +00:00
|
|
|
fileName := fmt.Sprintf("%s_%d", fileNamePre, index)
|
2018-01-09 05:39:24 +00:00
|
|
|
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)
|
|
|
|
}
|
2016-09-23 01:51:12 +00:00
|
|
|
|
|
|
|
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
|
2018-01-09 05:39:24 +00:00
|
|
|
if symlink {
|
|
|
|
testCase.writeToFile(linkedDirName, fileName, t)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-09-23 01:51:12 +00:00
|
|
|
testCase.writeToFile(dirName, fileName, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
go addFile()
|
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
// For !watchDir: expect an update by SourceFile.reloadConfig().
|
2016-10-03 22:07:08 +00:00
|
|
|
// 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)
|
2016-09-23 01:51:12 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 05:39:24 +00:00
|
|
|
func watchFileChanged(watchDir bool, symlink bool, t *testing.T) {
|
2016-09-23 01:51:12 +00:00
|
|
|
hostname := types.NodeName("random-test-hostname")
|
|
|
|
var testCases = getTestCases(hostname)
|
|
|
|
|
2017-06-16 08:43:52 +00:00
|
|
|
fileNamePre := "test_pod_manifest"
|
2016-09-23 01:51:12 +00:00
|
|
|
for index, testCase := range testCases {
|
|
|
|
func() {
|
2018-01-09 05:39:24 +00:00
|
|
|
dirName, err := mkTempDir("dir-test")
|
2016-09-23 01:51:12 +00:00
|
|
|
fileName := fmt.Sprintf("%s_%d", fileNamePre, index)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create temp dir: %v", err)
|
|
|
|
}
|
2018-01-09 05:39:24 +00:00
|
|
|
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)
|
|
|
|
}
|
2016-09-23 01:51:12 +00:00
|
|
|
|
|
|
|
var file string
|
|
|
|
ch := make(chan interface{})
|
|
|
|
func() {
|
2018-06-04 03:58:43 +00:00
|
|
|
testCase.lock.Lock()
|
|
|
|
defer testCase.lock.Unlock()
|
2018-01-09 05:39:24 +00:00
|
|
|
|
|
|
|
if symlink {
|
|
|
|
file = testCase.writeToFile(linkedDirName, fileName, t)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-09-23 01:51:12 +00:00
|
|
|
file = testCase.writeToFile(dirName, fileName, t)
|
|
|
|
}()
|
|
|
|
|
|
|
|
if watchDir {
|
|
|
|
NewSourceFile(dirName, hostname, 100*time.Millisecond, ch)
|
|
|
|
} else {
|
|
|
|
NewSourceFile(file, hostname, 100*time.Millisecond, ch)
|
|
|
|
}
|
|
|
|
// expect an update by SourceFile.resetStoreFromPath()
|
|
|
|
expectUpdate(t, ch, testCase)
|
|
|
|
|
|
|
|
changeFile := func() {
|
|
|
|
// Edit the file content
|
2018-06-04 03:58:43 +00:00
|
|
|
testCase.lock.Lock()
|
|
|
|
defer testCase.lock.Unlock()
|
2016-09-23 01:51:12 +00:00
|
|
|
|
2016-11-18 20:50:58 +00:00
|
|
|
pod := testCase.pod.(*v1.Pod)
|
2016-09-23 01:51:12 +00:00
|
|
|
pod.Spec.Containers[0].Name = "image2"
|
|
|
|
|
|
|
|
testCase.expected.Pods[0].Spec.Containers[0].Name = "image2"
|
2018-01-09 05:39:24 +00:00
|
|
|
if symlink {
|
|
|
|
file = testCase.writeToFile(linkedDirName, fileName, t)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
file = testCase.writeToFile(dirName, fileName, t)
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
go changeFile()
|
|
|
|
// expect an update by MODIFY inotify event
|
|
|
|
expectUpdate(t, ch, testCase)
|
|
|
|
|
|
|
|
if watchDir {
|
2018-06-04 03:58:43 +00:00
|
|
|
go changeFileName(dirName, fileName, fileName+"_ch", t)
|
2016-09-23 01:51:12 +00:00
|
|
|
// 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 expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) {
|
|
|
|
timer := time.After(5 * time.Second)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case got := <-ch:
|
|
|
|
update := got.(kubetypes.PodUpdate)
|
2018-01-09 05:39:24 +00:00
|
|
|
if len(update.Pods) == 0 {
|
|
|
|
// filter out the empty updates from reading a non-existing path
|
|
|
|
continue
|
|
|
|
}
|
2016-09-23 01:51:12 +00:00
|
|
|
for _, pod := range update.Pods {
|
2016-11-18 20:50:58 +00:00
|
|
|
// TODO: remove the conversion when validation is performed on versioned objects.
|
|
|
|
internalPod := &api.Pod{}
|
2017-10-09 17:13:46 +00:00
|
|
|
if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
|
2016-11-18 20:50:58 +00:00
|
|
|
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)
|
2016-09-23 01:51:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-04 03:58:43 +00:00
|
|
|
testCase.lock.Lock()
|
|
|
|
defer testCase.lock.Unlock()
|
2017-01-25 13:39:54 +00:00
|
|
|
if !apiequality.Semantic.DeepEqual(testCase.expected, update) {
|
2016-09-23 01:51:12 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|