Update go dependencies

This commit is contained in:
Manuel de Brito Fontes 2018-05-26 11:27:53 -04:00 committed by Manuel Alejandro de Brito Fontes
parent 15ffb51394
commit bb4d483837
No known key found for this signature in database
GPG key ID: 786136016A8BA02A
1621 changed files with 86368 additions and 284392 deletions

View file

@ -1,149 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package file
import (
"os"
"path/filepath"
"sort"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)
func RecoverEnv(wd, tmpDir string) {
os.Chdir(wd)
os.RemoveAll(tmpDir)
}
func TestFileUtils(t *testing.T) {
fs := &afero.Afero{Fs: afero.NewOsFs()}
// Create tmp dir
tmpDir, err := fs.TempDir(os.TempDir(), "util_file_test_")
if err != nil {
t.Fatal("Failed to test: failed to create temp dir.")
}
// create tmp file
tmpFile, err := fs.TempFile(tmpDir, "test_file_exists_")
if err != nil {
t.Fatal("Failed to test: failed to create temp file.")
}
// create tmp sym link
tmpSymlinkName := filepath.Join(tmpDir, "test_file_exists_sym_link")
err = os.Symlink(tmpFile.Name(), tmpSymlinkName)
if err != nil {
t.Fatal("Failed to test: failed to create sym link.")
}
// create tmp sub dir
tmpSubDir, err := fs.TempDir(tmpDir, "sub_")
if err != nil {
t.Fatal("Failed to test: failed to create temp sub dir.")
}
// record the current dir
currentDir, err := os.Getwd()
if err != nil {
t.Fatal("Failed to test: failed to get current dir.")
}
// change the work dir to temp dir
err = os.Chdir(tmpDir)
if err != nil {
t.Fatal("Failed to test: failed to change work dir.")
}
// recover test environment
defer RecoverEnv(currentDir, tmpDir)
t.Run("TestFileExists", func(t *testing.T) {
tests := []struct {
name string
fileName string
expectedError bool
expectedValue bool
}{
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
{"file_exists", tmpFile.Name(), false, true},
}
for _, test := range tests {
realValued, realError := FileExists(test.fileName)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
}
}
})
t.Run("TestFileOrSymlinkExists", func(t *testing.T) {
tests := []struct {
name string
fileName string
expectedError bool
expectedValue bool
}{
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
{"file_exists", tmpFile.Name(), false, true},
{"symlink_exists", tmpSymlinkName, false, true},
}
for _, test := range tests {
realValued, realError := FileOrSymlinkExists(test.fileName)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
}
}
})
t.Run("TestReadDirNoStat", func(t *testing.T) {
_, tmpFileSimpleName := filepath.Split(tmpFile.Name())
_, tmpSymlinkSimpleName := filepath.Split(tmpSymlinkName)
_, tmpSubDirSimpleName := filepath.Split(tmpSubDir)
tests := []struct {
name string
dirName string
expectedError bool
expectedValue []string
}{
{"dir_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), true, []string{}},
{"dir_is_empty", "", false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
{"dir_exists", tmpDir, false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
}
for _, test := range tests {
realValued, realError := ReadDirNoStat(test.dirName)
// execute sort action before compare
sort.Strings(realValued)
sort.Strings(test.expectedValue)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.dirName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.dirName, test.name)
}
}
})
}

View file

@ -1,147 +0,0 @@
/*
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 hash
import (
"fmt"
"hash/adler32"
"testing"
"github.com/davecgh/go-spew/spew"
)
type A struct {
x int
y string
}
type B struct {
x []int
y map[string]bool
}
type C struct {
x int
y string
}
func (c C) String() string {
return fmt.Sprintf("%d:%s", c.x, c.y)
}
func TestDeepHashObject(t *testing.T) {
successCases := []func() interface{}{
func() interface{} { return 8675309 },
func() interface{} { return "Jenny, I got your number" },
func() interface{} { return []string{"eight", "six", "seven"} },
func() interface{} { return [...]int{5, 3, 0, 9} },
func() interface{} { return map[int]string{8: "8", 6: "6", 7: "7"} },
func() interface{} { return map[string]int{"5": 5, "3": 3, "0": 0, "9": 9} },
func() interface{} { return A{867, "5309"} },
func() interface{} { return &A{867, "5309"} },
func() interface{} {
return B{[]int{8, 6, 7}, map[string]bool{"5": true, "3": true, "0": true, "9": true}}
},
func() interface{} { return map[A]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[C]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[*A]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[*C]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
}
for _, tc := range successCases {
hasher1 := adler32.New()
DeepHashObject(hasher1, tc())
hash1 := hasher1.Sum32()
DeepHashObject(hasher1, tc())
hash2 := hasher1.Sum32()
if hash1 != hash2 {
t.Fatalf("hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash2)
}
for i := 0; i < 100; i++ {
hasher2 := adler32.New()
DeepHashObject(hasher1, tc())
hash1a := hasher1.Sum32()
DeepHashObject(hasher2, tc())
hash2a := hasher2.Sum32()
if hash1a != hash1 {
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash1a)
}
if hash2a != hash2 {
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash2, hash2a)
}
if hash1a != hash2a {
t.Errorf("hash of the same object produced (%q) different results: %d vs %d", toString(tc()), hash1a, hash2a)
}
}
}
}
func toString(obj interface{}) string {
return spew.Sprintf("%#v", obj)
}
type wheel struct {
radius uint32
}
type unicycle struct {
primaryWheel *wheel
licencePlateID string
tags map[string]string
}
func TestDeepObjectPointer(t *testing.T) {
// Arrange
wheel1 := wheel{radius: 17}
wheel2 := wheel{radius: 22}
wheel3 := wheel{radius: 17}
myUni1 := unicycle{licencePlateID: "blah", primaryWheel: &wheel1, tags: map[string]string{"color": "blue", "name": "john"}}
myUni2 := unicycle{licencePlateID: "blah", primaryWheel: &wheel2, tags: map[string]string{"color": "blue", "name": "john"}}
myUni3 := unicycle{licencePlateID: "blah", primaryWheel: &wheel3, tags: map[string]string{"color": "blue", "name": "john"}}
// Run it more than once to verify determinism of hasher.
for i := 0; i < 100; i++ {
hasher1 := adler32.New()
hasher2 := adler32.New()
hasher3 := adler32.New()
// Act
DeepHashObject(hasher1, myUni1)
hash1 := hasher1.Sum32()
DeepHashObject(hasher1, myUni1)
hash1a := hasher1.Sum32()
DeepHashObject(hasher2, myUni2)
hash2 := hasher2.Sum32()
DeepHashObject(hasher3, myUni3)
hash3 := hasher3.Sum32()
// Assert
if hash1 != hash1a {
t.Errorf("repeated hash of the same object produced different results: %d vs %d", hash1, hash1a)
}
if hash1 == hash2 {
t.Errorf("hash1 (%d) and hash2(%d) must be different because they have different values for wheel size", hash1, hash2)
}
if hash1 != hash3 {
t.Errorf("hash1 (%d) and hash3(%d) must be the same because although they point to different objects, they have the same values for wheel size", hash1, hash3)
}
}
}

View file

@ -102,6 +102,7 @@ go_test(
] + select({
"@io_bazel_rules_go//go/platform:linux": [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
"@io_bazel_rules_go//go/platform:windows": [
"//vendor/github.com/stretchr/testify/assert:go_default_library",

View file

@ -1,165 +0,0 @@
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mount
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
)
var (
sourcePath = "/mnt/srv"
destinationPath = "/mnt/dst"
fsType = "xfs"
mountOptions = []string{"vers=1", "foo=bar"}
)
func TestMount(t *testing.T) {
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
if cmd != "mount" {
t.Errorf("expected mount command, got %q", cmd)
}
// mount -t fstype -o options source target
expectedArgs := []string{"-t", fsType, "-o", strings.Join(mountOptions, ","), sourcePath, destinationPath}
if !reflect.DeepEqual(expectedArgs, args) {
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
}
return nil, nil
})
wrappedMounter := &fakeMounter{t}
mounter := NewExecMounter(exec, wrappedMounter)
mounter.Mount(sourcePath, destinationPath, fsType, mountOptions)
}
func TestBindMount(t *testing.T) {
cmdCount := 0
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
cmdCount++
if cmd != "mount" {
t.Errorf("expected mount command, got %q", cmd)
}
var expectedArgs []string
switch cmdCount {
case 1:
// mount -t fstype -o "bind" source target
expectedArgs = []string{"-t", fsType, "-o", "bind", sourcePath, destinationPath}
case 2:
// mount -t fstype -o "remount,opts" source target
expectedArgs = []string{"-t", fsType, "-o", "remount," + strings.Join(mountOptions, ","), sourcePath, destinationPath}
}
if !reflect.DeepEqual(expectedArgs, args) {
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
}
return nil, nil
})
wrappedMounter := &fakeMounter{t}
mounter := NewExecMounter(exec, wrappedMounter)
bindOptions := append(mountOptions, "bind")
mounter.Mount(sourcePath, destinationPath, fsType, bindOptions)
}
func TestUnmount(t *testing.T) {
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
if cmd != "umount" {
t.Errorf("expected unmount command, got %q", cmd)
}
// unmount $target
expectedArgs := []string{destinationPath}
if !reflect.DeepEqual(expectedArgs, args) {
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
}
return nil, nil
})
wrappedMounter := &fakeMounter{t}
mounter := NewExecMounter(exec, wrappedMounter)
mounter.Unmount(destinationPath)
}
/* Fake wrapped mounter */
type fakeMounter struct {
t *testing.T
}
func (fm *fakeMounter) Mount(source string, target string, fstype string, options []string) error {
// Mount() of wrapped mounter should never be called. We call exec instead.
fm.t.Errorf("Unexpected wrapped mount call")
return fmt.Errorf("Unexpected wrapped mount call")
}
func (fm *fakeMounter) Unmount(target string) error {
// umount() of wrapped mounter should never be called. We call exec instead.
fm.t.Errorf("Unexpected wrapped mount call")
return fmt.Errorf("Unexpected wrapped mount call")
}
func (fm *fakeMounter) List() ([]MountPoint, error) {
return nil, nil
}
func (fm *fakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
return false
}
func (fm *fakeMounter) IsNotMountPoint(file string) (bool, error) {
return false, nil
}
func (fm *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return false, nil
}
func (fm *fakeMounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
func (fm *fakeMounter) PathIsDevice(pathname string) (bool, error) {
return false, nil
}
func (fm *fakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
return "", nil
}
func (fm *fakeMounter) MakeRShared(path string) error {
return nil
}
func (fm *fakeMounter) MakeFile(pathname string) error {
return nil
}
func (fm *fakeMounter) MakeDir(pathname string) error {
return nil
}
func (fm *fakeMounter) ExistsPath(pathname string) bool {
return false
}
func (fm *fakeMounter) GetFileType(pathname string) (FileType, error) {
return FileTypeFile, nil
}
func (fm *fakeMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
return subPath.Path, nil, nil
}
func (fm *fakeMounter) CleanSubPaths(podDir string, volumeName string) error {
return nil
}
func (fm *fakeMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
return nil
}

View file

@ -58,8 +58,10 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
f.mutex.Lock()
defer f.mutex.Unlock()
// find 'bind' option
opts := []string{}
for _, option := range options {
// find 'bind' option
if option == "bind" {
// This is a bind-mount. In order to mimic linux behaviour, we must
// use the original device of the bind-mount as the real source.
@ -78,7 +80,11 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
break
}
}
break
}
// find 'ro' option
if option == "ro" {
// reuse MountPoint.Opts field to mark mount as readonly
opts = append(opts, "ro")
}
}
@ -88,7 +94,7 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
absTarget = target
}
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype})
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: opts})
glog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
return nil

View file

@ -19,6 +19,7 @@ limitations under the License.
package mount
import (
"fmt"
"os"
"path/filepath"
"strings"
@ -123,6 +124,8 @@ type Subpath struct {
PodDir string
// Name of the container
ContainerName string
// True if the mount needs to be readonly
ReadOnly bool
}
// Exec executes command where mount utilities are. This can be either the host,
@ -281,7 +284,13 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
// The list equals:
// options - 'bind' + 'remount' (no duplicate)
func isBind(options []string) (bool, []string) {
bindRemountOpts := []string{"remount"}
// Because we have an FD opened on the subpath bind mount, the "bind" option
// needs to be included, otherwise the mount target will error as busy if you
// remount as readonly.
//
// As a consequence, all read only bind mounts will no longer change the underlying
// volume mount to be read only.
bindRemountOpts := []string{"bind", "remount"}
bind := false
if len(options) != 0 {
@ -337,3 +346,37 @@ func startsWithBackstep(rel string) bool {
// normalize to / and check for ../
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
}
// getFileType checks for file/directory/socket and block/character devices
func getFileType(pathname string) (FileType, error) {
var pathType FileType
info, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
// checks whether the mode is the target mode
isSpecificMode := func(mode, targetMode os.FileMode) bool {
return mode&targetMode == targetMode
}
mode := info.Mode()
if mode.IsDir() {
return FileTypeDirectory, nil
} else if mode.IsRegular() {
return FileTypeFile, nil
} else if isSpecificMode(mode, os.ModeSocket) {
return FileTypeSocket, nil
} else if isSpecificMode(mode, os.ModeDevice) {
if isSpecificMode(mode, os.ModeCharDevice) {
return FileTypeCharDev, nil
}
return FileTypeBlockDev, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

View file

@ -423,31 +423,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
}
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := finfo.Sys().(*syscall.Stat_t).Mode
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeCharDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
return getFileType(pathname)
}
func (mounter *Mounter) MakeDir(pathname string) error {
@ -801,8 +777,13 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath
mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd)
// Do the bind mount
options := []string{"bind"}
if subpath.ReadOnly {
options = append(options, "ro")
}
glog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, []string{"bind"}); err != nil {
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil {
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
}

File diff suppressed because it is too large Load diff

View file

@ -201,31 +201,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
// GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
info, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeCharDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
return getFileType(pathname)
}
// MakeFile creates a new directory

View file

@ -1,553 +0,0 @@
// +build windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mount
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNormalizeWindowsPath(t *testing.T) {
path := `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4/volumes/kubernetes.io~azure-disk`
normalizedPath := normalizeWindowsPath(path)
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
}
path = `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk`
normalizedPath = normalizeWindowsPath(path)
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
}
path = `/`
normalizedPath = normalizeWindowsPath(path)
if normalizedPath != `c:\` {
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
}
}
func TestValidateDiskNumber(t *testing.T) {
diskNum := "0"
if err := ValidateDiskNumber(diskNum); err != nil {
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
}
diskNum = "99"
if err := ValidateDiskNumber(diskNum); err != nil {
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
}
diskNum = "ab"
if err := ValidateDiskNumber(diskNum); err == nil {
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
}
diskNum = "100"
if err := ValidateDiskNumber(diskNum); err == nil {
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
}
}
func makeLink(link, target string) error {
if output, err := exec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput(); err != nil {
return fmt.Errorf("mklink failed: %v, link(%q) target(%q) output: %q", err, link, target, string(output))
}
return nil
}
func removeLink(link string) error {
if output, err := exec.Command("cmd", "/c", "rmdir", link).CombinedOutput(); err != nil {
return fmt.Errorf("rmdir failed: %v, output: %q", err, string(output))
}
return nil
}
func setEquivalent(set1, set2 []string) bool {
map1 := make(map[string]bool)
map2 := make(map[string]bool)
for _, s := range set1 {
map1[s] = true
}
for _, s := range set2 {
map2[s] = true
}
for s := range map1 {
if !map2[s] {
return false
}
}
for s := range map2 {
if !map1[s] {
return false
}
}
return true
}
// this func must run in admin mode, otherwise it will fail
func TestGetMountRefs(t *testing.T) {
fm := &FakeMounter{MountPoints: []MountPoint{}}
mountPath := `c:\secondmountpath`
expectedRefs := []string{`c:\`, `c:\firstmountpath`, mountPath}
// remove symbolic links first
for i := 1; i < len(expectedRefs); i++ {
removeLink(expectedRefs[i])
}
// create symbolic links
for i := 1; i < len(expectedRefs); i++ {
if err := makeLink(expectedRefs[i], expectedRefs[i-1]); err != nil {
t.Errorf("makeLink failed: %v", err)
}
}
if refs, err := GetMountRefs(fm, mountPath); err != nil || !setEquivalent(expectedRefs, refs) {
t.Errorf("getMountRefs(%q) = %v, error: %v; expected %v", mountPath, refs, err, expectedRefs)
}
// remove symbolic links
for i := 1; i < len(expectedRefs); i++ {
if err := removeLink(expectedRefs[i]); err != nil {
t.Errorf("removeLink failed: %v", err)
}
}
}
func TestDoSafeMakeDir(t *testing.T) {
const testingVolumePath = `c:\tmp\DoSafeMakeDirTest`
os.MkdirAll(testingVolumePath, 0755)
defer os.RemoveAll(testingVolumePath)
tests := []struct {
volumePath string
subPath string
expectError bool
symlinkTarget string
}{
{
volumePath: testingVolumePath,
subPath: ``,
expectError: true,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `x`),
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `symlink`),
expectError: false,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `symlink\c\d`),
expectError: true,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `symlink\y926`),
expectError: true,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\symlink`),
expectError: false,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\x\symlink`),
expectError: false,
symlinkTarget: filepath.Join(testingVolumePath, `a`),
},
}
for _, test := range tests {
if len(test.volumePath) > 0 && len(test.subPath) > 0 && len(test.symlinkTarget) > 0 {
// make all parent sub directories
if parent := filepath.Dir(test.subPath); parent != "." {
os.MkdirAll(parent, 0755)
}
// make last element as symlink
linkPath := test.subPath
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
}
}
}
err := doSafeMakeDir(test.subPath, test.volumePath, os.FileMode(0755))
if test.expectError {
assert.NotNil(t, err, "Expect error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
continue
}
assert.Nil(t, err, "Expect no error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
if _, err := os.Stat(test.subPath); os.IsNotExist(err) {
t.Errorf("subPath should exists after doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
}
}
}
func TestLockAndCheckSubPath(t *testing.T) {
const testingVolumePath = `c:\tmp\LockAndCheckSubPathTest`
tests := []struct {
volumePath string
subPath string
expectedHandleCount int
expectError bool
symlinkTarget string
}{
{
volumePath: `c:\`,
subPath: ``,
expectedHandleCount: 0,
expectError: false,
symlinkTarget: "",
},
{
volumePath: ``,
subPath: `a`,
expectedHandleCount: 0,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a`),
expectedHandleCount: 1,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
expectedHandleCount: 4,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `symlink`),
expectedHandleCount: 0,
expectError: true,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
expectedHandleCount: 0,
expectError: true,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
expectedHandleCount: 2,
expectError: false,
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
},
}
for _, test := range tests {
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
os.MkdirAll(test.volumePath, 0755)
if len(test.symlinkTarget) == 0 {
// make all intermediate sub directories
os.MkdirAll(test.subPath, 0755)
} else {
// make all parent sub directories
if parent := filepath.Dir(test.subPath); parent != "." {
os.MkdirAll(parent, 0755)
}
// make last element as symlink
linkPath := test.subPath
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
}
}
}
}
fileHandles, err := lockAndCheckSubPath(test.volumePath, test.subPath)
unlockPath(fileHandles)
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
if test.expectError {
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
continue
}
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
}
// remove dir will happen after closing all file handles
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
}
func TestLockAndCheckSubPathWithoutSymlink(t *testing.T) {
const testingVolumePath = `c:\tmp\LockAndCheckSubPathWithoutSymlinkTest`
tests := []struct {
volumePath string
subPath string
expectedHandleCount int
expectError bool
symlinkTarget string
}{
{
volumePath: `c:\`,
subPath: ``,
expectedHandleCount: 0,
expectError: false,
symlinkTarget: "",
},
{
volumePath: ``,
subPath: `a`,
expectedHandleCount: 0,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a`),
expectedHandleCount: 1,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
expectedHandleCount: 4,
expectError: false,
symlinkTarget: "",
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `symlink`),
expectedHandleCount: 1,
expectError: true,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
expectedHandleCount: 4,
expectError: true,
symlinkTarget: `c:\tmp`,
},
{
volumePath: testingVolumePath,
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
expectedHandleCount: 5,
expectError: true,
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
},
}
for _, test := range tests {
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
os.MkdirAll(test.volumePath, 0755)
if len(test.symlinkTarget) == 0 {
// make all intermediate sub directories
os.MkdirAll(test.subPath, 0755)
} else {
// make all parent sub directories
if parent := filepath.Dir(test.subPath); parent != "." {
os.MkdirAll(parent, 0755)
}
// make last element as symlink
linkPath := test.subPath
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
}
}
}
}
fileHandles, err := lockAndCheckSubPathWithoutSymlink(test.volumePath, test.subPath)
unlockPath(fileHandles)
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
if test.expectError {
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
continue
}
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
}
// remove dir will happen after closing all file handles
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
}
func TestFindExistingPrefix(t *testing.T) {
const testingVolumePath = `c:\tmp\FindExistingPrefixTest`
tests := []struct {
base string
pathname string
expectError bool
expectedExistingPath string
expectedToCreateDirs []string
createSubPathBeforeTest bool
}{
{
base: `c:\tmp\a`,
pathname: `c:\tmp\b`,
expectError: true,
expectedExistingPath: "",
expectedToCreateDirs: []string{},
createSubPathBeforeTest: false,
},
{
base: ``,
pathname: `c:\tmp\b`,
expectError: true,
expectedExistingPath: "",
expectedToCreateDirs: []string{},
createSubPathBeforeTest: false,
},
{
base: `c:\tmp\a`,
pathname: `d:\tmp\b`,
expectError: true,
expectedExistingPath: "",
expectedToCreateDirs: []string{},
createSubPathBeforeTest: false,
},
{
base: testingVolumePath,
pathname: testingVolumePath,
expectError: false,
expectedExistingPath: testingVolumePath,
expectedToCreateDirs: []string{},
createSubPathBeforeTest: false,
},
{
base: testingVolumePath,
pathname: filepath.Join(testingVolumePath, `a\b`),
expectError: false,
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
expectedToCreateDirs: []string{},
createSubPathBeforeTest: true,
},
{
base: testingVolumePath,
pathname: filepath.Join(testingVolumePath, `a\b\c\`),
expectError: false,
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
expectedToCreateDirs: []string{`c`},
createSubPathBeforeTest: false,
},
{
base: testingVolumePath,
pathname: filepath.Join(testingVolumePath, `a\b\c\d`),
expectError: false,
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
expectedToCreateDirs: []string{`c`, `d`},
createSubPathBeforeTest: false,
},
}
for _, test := range tests {
if test.createSubPathBeforeTest {
os.MkdirAll(test.pathname, 0755)
}
existingPath, toCreate, err := findExistingPrefix(test.base, test.pathname)
if test.expectError {
assert.NotNil(t, err, "Expect error during findExistingPrefix(%s, %s)", test.base, test.pathname)
continue
}
assert.Nil(t, err, "Expect no error during findExistingPrefix(%s, %s)", test.base, test.pathname)
assert.Equal(t, test.expectedExistingPath, existingPath, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
test.base, test.pathname, existingPath, test.expectedExistingPath)
assert.Equal(t, test.expectedToCreateDirs, toCreate, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
test.base, test.pathname, toCreate, test.expectedToCreateDirs)
}
// remove dir will happen after closing all file handles
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
}
func TestPathWithinBase(t *testing.T) {
tests := []struct {
fullPath string
basePath string
expectedResult bool
}{
{
fullPath: `c:\tmp\a\b\c`,
basePath: `c:\tmp`,
expectedResult: true,
},
{
fullPath: `c:\tmp1`,
basePath: `c:\tmp2`,
expectedResult: false,
},
{
fullPath: `c:\tmp`,
basePath: `c:\tmp`,
expectedResult: true,
},
{
fullPath: `c:\tmp`,
basePath: `c:\tmp\a\b\c`,
expectedResult: false,
},
{
fullPath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config\..timestamp\file.txt`,
basePath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config`,
expectedResult: true,
},
}
for _, test := range tests {
result := pathWithinBase(test.fullPath, test.basePath)
assert.Equal(t, result, test.expectedResult, "Expect result not equal with pathWithinBase(%s, %s) return: %q, expected: %q",
test.fullPath, test.basePath, result, test.expectedResult)
}
}

View file

@ -235,8 +235,13 @@ func (n *NsenterMounter) MakeRShared(path string) error {
func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", "--printf=%F", pathname}).CombinedOutput()
if err != nil {
if strings.Contains(string(outputBytes), "No such file") {
err = fmt.Errorf("%s does not exist", pathname)
} else {
err = fmt.Errorf("stat %s error: %v", pathname, string(outputBytes))
}
return pathType, err
}

View file

@ -1,191 +0,0 @@
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mount
import (
"io/ioutil"
"os"
"path"
"strconv"
"testing"
)
func TestParseFindMnt(t *testing.T) {
tests := []struct {
input string
target string
expectError bool
}{
{
// standard mount name, e.g. for AWS
"/var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-1d/vol-020f82b0759f72389 ext4\n",
"/var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-1d/vol-020f82b0759f72389",
false,
},
{
// mount name with space, e.g. vSphere
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[datastore1] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk ext2\n",
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[datastore1] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk",
false,
},
{
// hypotetic mount with several spaces
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[ d a t a s t o r e 1 ] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk ext2\n",
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[ d a t a s t o r e 1 ] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk",
false,
},
{
// invalid output - no filesystem type
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/blabla",
"",
true,
},
}
for i, test := range tests {
target, err := parseFindMnt(test.input)
if test.expectError && err == nil {
t.Errorf("test %d expected error, got nil", i)
}
if !test.expectError && err != nil {
t.Errorf("test %d returned error: %s", i, err)
}
if target != test.target {
t.Errorf("test %d expected %q, got %q", i, test.target, target)
}
}
}
func TestGetPidOnHost(t *testing.T) {
tempDir, err := ioutil.TempDir("", "get_pid_on_host_tests")
if err != nil {
t.Fatalf(err.Error())
}
defer os.RemoveAll(tempDir)
tests := []struct {
name string
procFile string
expectedPid int
expectError bool
}{
{
name: "valid status file",
procFile: `Name: cat
Umask: 0002
State: R (running)
Tgid: 15041
Ngid: 0
Pid: 15041
PPid: 22699
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 10 135 156 157 158 973 984 1000 1001
NStgid: 15041
NSpid: 15041
NSpgid: 15041
NSsid: 22699
VmPeak: 115016 kB
VmSize: 115016 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 816 kB
VmRSS: 816 kB
RssAnon: 64 kB
RssFile: 752 kB
RssShmem: 0 kB
VmData: 312 kB
VmStk: 136 kB
VmExe: 32 kB
VmLib: 2060 kB
VmPTE: 44 kB
VmPMD: 12 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
Threads: 1
SigQ: 2/60752
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Cpus_allowed: ff
Cpus_allowed_list: 0-7
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 0
nonvoluntary_ctxt_switches: 0
`,
expectedPid: 15041,
},
{
name: "no Pid:",
procFile: `Name: cat
Umask: 0002
State: R (running)
Tgid: 15041
Ngid: 0
PPid: 22699
`,
expectedPid: 0,
expectError: true,
},
{
name: "invalid Pid:",
procFile: `Name: cat
Umask: 0002
State: R (running)
Tgid: 15041
Ngid: 0
Pid: invalid
PPid: 22699
`,
expectedPid: 0,
expectError: true,
},
}
for i, test := range tests {
filename := path.Join(tempDir, strconv.Itoa(i))
err := ioutil.WriteFile(filename, []byte(test.procFile), 0666)
if err != nil {
t.Fatalf(err.Error())
}
mounter := NsenterMounter{}
pid, err := mounter.getPidOnHost(filename)
if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error: %s", test.name, err)
}
if err == nil && test.expectError {
t.Errorf("Test %q: expected error, got none", test.name)
}
if pid != test.expectedPid {
t.Errorf("Test %q: expected pid %d, got %d", test.name, test.expectedPid, pid)
}
}
}

View file

@ -1,242 +0,0 @@
/*
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 mount
import (
"fmt"
"io/ioutil"
"os"
"runtime"
"testing"
fakeexec "k8s.io/utils/exec/testing"
)
type ErrorMounter struct {
*FakeMounter
errIndex int
err []error
}
func (mounter *ErrorMounter) Mount(source string, target string, fstype string, options []string) error {
i := mounter.errIndex
mounter.errIndex++
if mounter.err != nil && mounter.err[i] != nil {
return mounter.err[i]
}
return mounter.FakeMounter.Mount(source, target, fstype, options)
}
type ExecArgs struct {
command string
args []string
output string
err error
}
func TestSafeFormatAndMount(t *testing.T) {
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
}
mntDir, err := ioutil.TempDir(os.TempDir(), "mount")
if err != nil {
t.Fatalf("failed to create tmp dir: %v", err)
}
defer os.RemoveAll(mntDir)
tests := []struct {
description string
fstype string
mountOptions []string
execScripts []ExecArgs
mountErrs []error
expectedError error
}{
{
description: "Test a read only mount",
fstype: "ext4",
mountOptions: []string{"ro"},
},
{
description: "Test a normal mount",
fstype: "ext4",
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
},
},
{
description: "Test 'fsck' fails with exit status 4",
fstype: "ext4",
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}},
},
expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them: ."),
},
{
description: "Test 'fsck' fails with exit status 1 (errors found and corrected)",
fstype: "ext4",
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 1}},
},
},
{
description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)",
fstype: "ext4",
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 8}},
},
},
{
description: "Test that 'blkid' is called and fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nTYPE=ext4\n", nil},
},
expectedError: fmt.Errorf("unknown filesystem type '(null)'"),
},
{
description: "Test that 'blkid' is called and confirms unformatted disk, format fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
},
expectedError: fmt.Errorf("formatting failed"),
},
{
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: fmt.Errorf("Still cannot mount"),
},
{
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: nil,
},
{
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes with ext3",
fstype: "ext3",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext3", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: nil,
},
{
description: "test that none ext4 fs does not get called with ext4 options.",
fstype: "xfs",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
},
expectedError: nil,
},
{
description: "Test that 'blkid' is called and reports ext4 partition",
fstype: "ext3",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nPTTYPE=dos\n", nil},
},
expectedError: fmt.Errorf("failed to mount the volume as \"ext3\", it already contains unknown data, probably partitions. Mount error: unknown filesystem type '(null)'"),
},
{
description: "Test that 'blkid' is called but has some usage or other errors (an exit code of 4 is returned)",
fstype: "xfs",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}},
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
},
expectedError: fmt.Errorf("exit 4"),
},
}
for _, test := range tests {
execCallCount := 0
execCallback := func(cmd string, args ...string) ([]byte, error) {
if len(test.execScripts) <= execCallCount {
t.Errorf("Unexpected command: %s %v", cmd, args)
return nil, nil
}
script := test.execScripts[execCallCount]
execCallCount++
if script.command != cmd {
t.Errorf("Unexpected command %s. Expecting %s", cmd, script.command)
}
for j := range args {
if args[j] != script.args[j] {
t.Errorf("Unexpected args %v. Expecting %v", args, script.args)
}
}
return []byte(script.output), script.err
}
fakeMounter := ErrorMounter{&FakeMounter{}, 0, test.mountErrs}
fakeExec := NewFakeExec(execCallback)
mounter := SafeFormatAndMount{
Interface: &fakeMounter,
Exec: fakeExec,
}
device := "/dev/foo"
dest := mntDir
err := mounter.FormatAndMount(device, dest, test.fstype, test.mountOptions)
if test.expectedError == nil {
if err != nil {
t.Errorf("test \"%s\" unexpected non-error: %v", test.description, err)
}
// Check that something was mounted on the directory
isNotMountPoint, err := fakeMounter.IsLikelyNotMountPoint(dest)
if err != nil || isNotMountPoint {
t.Errorf("test \"%s\" the directory was not mounted", test.description)
}
//check that the correct device was mounted
mountedDevice, _, err := GetDeviceNameFromMount(fakeMounter.FakeMounter, dest)
if err != nil || mountedDevice != device {
t.Errorf("test \"%s\" the correct device was not mounted", test.description)
}
} else {
if err == nil || test.expectedError.Error() != err.Error() {
t.Errorf("test \"%s\" unexpected error: \n [%v]. \nExpecting [%v]", test.description, err, test.expectedError)
}
}
}
}

View file

@ -1,286 +0,0 @@
/*
Copyright 2018 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 net
import (
"net"
"reflect"
"testing"
)
func TestIsIPv6String(t *testing.T) {
testCases := []struct {
ip string
expectIPv6 bool
}{
{
ip: "127.0.0.1",
expectIPv6: false,
},
{
ip: "192.168.0.0",
expectIPv6: false,
},
{
ip: "1.2.3.4",
expectIPv6: false,
},
{
ip: "bad ip",
expectIPv6: false,
},
{
ip: "::1",
expectIPv6: true,
},
{
ip: "fd00::600d:f00d",
expectIPv6: true,
},
{
ip: "2001:db8::5",
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6String(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}
func TestIsIPv6(t *testing.T) {
testCases := []struct {
ip net.IP
expectIPv6 bool
}{
{
ip: net.IPv4zero,
expectIPv6: false,
},
{
ip: net.IPv4bcast,
expectIPv6: false,
},
{
ip: net.ParseIP("127.0.0.1"),
expectIPv6: false,
},
{
ip: net.ParseIP("10.20.40.40"),
expectIPv6: false,
},
{
ip: net.ParseIP("172.17.3.0"),
expectIPv6: false,
},
{
ip: nil,
expectIPv6: false,
},
{
ip: net.IPv6loopback,
expectIPv6: true,
},
{
ip: net.IPv6zero,
expectIPv6: true,
},
{
ip: net.ParseIP("fd00::600d:f00d"),
expectIPv6: true,
},
{
ip: net.ParseIP("2001:db8::5"),
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}
func TestIsIPv6CIDR(t *testing.T) {
testCases := []struct {
desc string
cidr string
expectResult bool
}{
{
desc: "ipv4 CIDR 1",
cidr: "10.0.0.0/8",
expectResult: false,
},
{
desc: "ipv4 CIDR 2",
cidr: "192.168.0.0/16",
expectResult: false,
},
{
desc: "ipv6 CIDR 1",
cidr: "::/1",
expectResult: true,
},
{
desc: "ipv6 CIDR 2",
cidr: "2000::/10",
expectResult: true,
},
{
desc: "ipv6 CIDR 3",
cidr: "2001:db8::/32",
expectResult: true,
},
}
for _, tc := range testCases {
res := IsIPv6CIDR(tc.cidr)
if res != tc.expectResult {
t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res)
}
}
}
func TestFilterIncorrectIPVersion(t *testing.T) {
testCases := []struct {
desc string
isIPv6 bool
ipStrings []string
expectCorrects []string
expectIncorrects []string
}{
{
desc: "all ipv4 strings in ipv4 mode",
isIPv6: false,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectIncorrects: nil,
},
{
desc: "all ipv6 strings in ipv4 mode",
isIPv6: false,
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: nil,
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
},
{
desc: "mixed versions in ipv4 mode",
isIPv6: false,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
},
{
desc: "all ipv4 strings in ipv6 mode",
isIPv6: true,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectCorrects: nil,
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
},
{
desc: "all ipv6 strings in ipv6 mode",
isIPv6: true,
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectIncorrects: nil,
},
{
desc: "mixed versions in ipv6 mode",
isIPv6: true,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
},
}
for _, tc := range testCases {
corrects, incorrects := FilterIncorrectIPVersion(tc.ipStrings, tc.isIPv6)
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
}
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
}
}
}
func TestFilterIncorrectCIDRVersion(t *testing.T) {
testCases := []struct {
desc string
isIPv6 bool
cidrStrings []string
expectCorrects []string
expectIncorrects []string
}{
{
desc: "all ipv4 strings in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectIncorrects: nil,
},
{
desc: "all ipv6 strings in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: nil,
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
},
{
desc: "mixed versions in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
},
{
desc: "all ipv4 strings in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectCorrects: nil,
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
},
{
desc: "all ipv6 strings in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectIncorrects: nil,
},
{
desc: "mixed versions in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
},
}
for _, tc := range testCases {
corrects, incorrects := FilterIncorrectCIDRVersion(tc.cidrStrings, tc.isIPv6)
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
}
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
}
}
}

View file

@ -1,155 +0,0 @@
/*
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 sets
import (
"net"
"reflect"
"sort"
"testing"
)
func parseIPNet(s string) *net.IPNet {
_, net, err := net.ParseCIDR(s)
if err != nil {
panic(err)
}
return net
}
func TestIPNets(t *testing.T) {
s := IPNet{}
s2 := IPNet{}
if len(s) != 0 {
t.Errorf("Expected len=0: %d", len(s))
}
a := parseIPNet("1.0.0.0/8")
b := parseIPNet("2.0.0.0/8")
c := parseIPNet("3.0.0.0/8")
d := parseIPNet("4.0.0.0/8")
s.Insert(a, b)
if len(s) != 2 {
t.Errorf("Expected len=2: %d", len(s))
}
s.Insert(c)
if s.Has(d) {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.Has(a) {
t.Errorf("Missing contents: %#v", s)
}
s.Delete(a)
if s.Has(a) {
t.Errorf("Unexpected contents: %#v", s)
}
s.Insert(a)
if s.HasAll(a, b, d) {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.HasAll(a, b) {
t.Errorf("Missing contents: %#v", s)
}
s2.Insert(a, b, d)
if s.IsSuperset(s2) {
t.Errorf("Unexpected contents: %#v", s)
}
s2.Delete(d)
if !s.IsSuperset(s2) {
t.Errorf("Missing contents: %#v", s)
}
}
func TestIPNetSetDeleteMultiples(t *testing.T) {
s := IPNet{}
a := parseIPNet("1.0.0.0/8")
b := parseIPNet("2.0.0.0/8")
c := parseIPNet("3.0.0.0/8")
s.Insert(a, b, c)
if len(s) != 3 {
t.Errorf("Expected len=3: %d", len(s))
}
s.Delete(a, c)
if len(s) != 1 {
t.Errorf("Expected len=1: %d", len(s))
}
if s.Has(a) {
t.Errorf("Unexpected contents: %#v", s)
}
if s.Has(c) {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.Has(b) {
t.Errorf("Missing contents: %#v", s)
}
}
func TestNewIPSet(t *testing.T) {
s, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
if err != nil {
t.Errorf("error parsing IPNets: %v", err)
}
if len(s) != 3 {
t.Errorf("Expected len=3: %d", len(s))
}
a := parseIPNet("1.0.0.0/8")
b := parseIPNet("2.0.0.0/8")
c := parseIPNet("3.0.0.0/8")
if !s.Has(a) || !s.Has(b) || !s.Has(c) {
t.Errorf("Unexpected contents: %#v", s)
}
}
func TestIPNetSetDifference(t *testing.T) {
l, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
if err != nil {
t.Errorf("error parsing IPNets: %v", err)
}
r, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "4.0.0.0/8", "5.0.0.0/8")
if err != nil {
t.Errorf("error parsing IPNets: %v", err)
}
c := l.Difference(r)
d := r.Difference(l)
if len(c) != 1 {
t.Errorf("Expected len=1: %d", len(c))
}
if !c.Has(parseIPNet("3.0.0.0/8")) {
t.Errorf("Unexpected contents: %#v", c)
}
if len(d) != 2 {
t.Errorf("Expected len=2: %d", len(d))
}
if !d.Has(parseIPNet("4.0.0.0/8")) || !d.Has(parseIPNet("5.0.0.0/8")) {
t.Errorf("Unexpected contents: %#v", d)
}
}
func TestIPNetSetList(t *testing.T) {
s, err := ParseIPNets("3.0.0.0/8", "1.0.0.0/8", "2.0.0.0/8")
if err != nil {
t.Errorf("error parsing IPNets: %v", err)
}
l := s.StringSlice()
sort.Strings(l)
if !reflect.DeepEqual(l, []string{"1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8"}) {
t.Errorf("List gave unexpected result: %#v", l)
}
}

View file

@ -1,51 +0,0 @@
/*
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 parsers
import (
"testing"
)
// Based on Docker test case removed in:
// https://github.com/docker/docker/commit/4352da7803d182a6013a5238ce20a7c749db979a
func TestParseImageName(t *testing.T) {
testCases := []struct {
Input string
Repo string
Tag string
Digest string
}{
{Input: "root", Repo: "docker.io/library/root", Tag: "latest"},
{Input: "root:tag", Repo: "docker.io/library/root", Tag: "tag"},
{Input: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "docker.io/library/root", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "user/repo", Repo: "docker.io/user/repo", Tag: "latest"},
{Input: "user/repo:tag", Repo: "docker.io/user/repo", Tag: "tag"},
{Input: "user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "docker.io/user/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{Input: "url:5000/repo", Repo: "url:5000/repo", Tag: "latest"},
{Input: "url:5000/repo:tag", Repo: "url:5000/repo", Tag: "tag"},
{Input: "url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "url:5000/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
}
for _, testCase := range testCases {
repo, tag, digest, err := ParseImageName(testCase.Input)
if err != nil {
t.Errorf("ParseImageName(%s) failed: %v", testCase.Input, err)
} else if repo != testCase.Repo || tag != testCase.Tag || digest != testCase.Digest {
t.Errorf("Expected repo: %q, tag: %q and digest: %q, got %q, %q and %q", testCase.Repo, testCase.Tag, testCase.Digest,
repo, tag, digest)
}
}
}

View file

@ -1,66 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pointer
import (
"testing"
)
func TestAllPtrFieldsNil(t *testing.T) {
testCases := []struct {
obj interface{}
expected bool
}{
{struct{}{}, true},
{struct{ Foo int }{12345}, true},
{&struct{ Foo int }{12345}, true},
{struct{ Foo *int }{nil}, true},
{&struct{ Foo *int }{nil}, true},
{struct {
Foo int
Bar *int
}{12345, nil}, true},
{&struct {
Foo int
Bar *int
}{12345, nil}, true},
{struct {
Foo *int
Bar *int
}{nil, nil}, true},
{&struct {
Foo *int
Bar *int
}{nil, nil}, true},
{struct{ Foo *int }{new(int)}, false},
{&struct{ Foo *int }{new(int)}, false},
{struct {
Foo *int
Bar *int
}{nil, new(int)}, false},
{&struct {
Foo *int
Bar *int
}{nil, new(int)}, false},
{(*struct{})(nil), true},
}
for i, tc := range testCases {
if AllPtrFieldsNil(tc.obj) != tc.expected {
t.Errorf("case[%d]: expected %t, got %t", i, tc.expected, !tc.expected)
}
}
}

View file

@ -1,687 +0,0 @@
/*
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 taints
import (
"reflect"
"strings"
"testing"
"k8s.io/api/core/v1"
api "k8s.io/kubernetes/pkg/apis/core"
"github.com/spf13/pflag"
)
func TestTaintsVar(t *testing.T) {
cases := []struct {
f string
err bool
t []api.Taint
}{
{
f: "",
t: []api.Taint(nil),
},
{
f: "--t=foo=bar:NoSchedule",
t: []api.Taint{{Key: "foo", Value: "bar", Effect: "NoSchedule"}},
},
{
f: "--t=foo=bar:NoSchedule,bing=bang:PreferNoSchedule",
t: []api.Taint{
{Key: "foo", Value: "bar", Effect: api.TaintEffectNoSchedule},
{Key: "bing", Value: "bang", Effect: api.TaintEffectPreferNoSchedule},
},
},
{
f: "--t=dedicated-for=user1:NoExecute",
t: []api.Taint{{Key: "dedicated-for", Value: "user1", Effect: "NoExecute"}},
},
}
for i, c := range cases {
args := append([]string{"test"}, strings.Fields(c.f)...)
cli := pflag.NewFlagSet("test", pflag.ContinueOnError)
var taints []api.Taint
cli.Var(NewTaintsVar(&taints), "t", "bar")
err := cli.Parse(args)
if err == nil && c.err {
t.Errorf("[%v] expected error", i)
continue
}
if err != nil && !c.err {
t.Errorf("[%v] unexpected error: %v", i, err)
continue
}
if !reflect.DeepEqual(c.t, taints) {
t.Errorf("[%v] unexpected taints:\n\texpected:\n\t\t%#v\n\tgot:\n\t\t%#v", i, c.t, taints)
}
}
}
func TestAddOrUpdateTaint(t *testing.T) {
node := &v1.Node{}
taint := &v1.Taint{
Key: "foo",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
}
checkResult := func(testCaseName string, newNode *v1.Node, expectedTaint *v1.Taint, result, expectedResult bool, err error) {
if err != nil {
t.Errorf("[%s] should not raise error but got %v", testCaseName, err)
}
if result != expectedResult {
t.Errorf("[%s] should return %t, but got: %t", testCaseName, expectedResult, result)
}
if len(newNode.Spec.Taints) != 1 || !reflect.DeepEqual(newNode.Spec.Taints[0], *expectedTaint) {
t.Errorf("[%s] node should only have one taint: %v, but got: %v", testCaseName, *expectedTaint, newNode.Spec.Taints)
}
}
// Add a new Taint.
newNode, result, err := AddOrUpdateTaint(node, taint)
checkResult("Add New Taint", newNode, taint, result, true, err)
// Update a Taint.
taint.Value = "bar_1"
newNode, result, err = AddOrUpdateTaint(node, taint)
checkResult("Update Taint", newNode, taint, result, true, err)
// Add a duplicate Taint.
node = newNode
newNode, result, err = AddOrUpdateTaint(node, taint)
checkResult("Add Duplicate Taint", newNode, taint, result, false, err)
}
func TestTaintExists(t *testing.T) {
testingTaints := []v1.Taint{
{
Key: "foo_1",
Value: "bar_1",
Effect: v1.TaintEffectNoExecute,
},
{
Key: "foo_2",
Value: "bar_2",
Effect: v1.TaintEffectNoSchedule,
},
}
cases := []struct {
name string
taintToFind *v1.Taint
expectedResult bool
}{
{
name: "taint exists",
taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoExecute},
expectedResult: true,
},
{
name: "different key",
taintToFind: &v1.Taint{Key: "no_such_key", Value: "bar_1", Effect: v1.TaintEffectNoExecute},
expectedResult: false,
},
{
name: "different effect",
taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoSchedule},
expectedResult: false,
},
}
for _, c := range cases {
result := TaintExists(testingTaints, c.taintToFind)
if result != c.expectedResult {
t.Errorf("[%s] unexpected results: %v", c.name, result)
continue
}
}
}
func TestRemoveTaint(t *testing.T) {
cases := []struct {
name string
node *v1.Node
taintToRemove *v1.Taint
expectedTaints []v1.Taint
expectedResult bool
}{
{
name: "remove taint unsuccessfully",
node: &v1.Node{
Spec: v1.NodeSpec{
Taints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
},
},
taintToRemove: &v1.Taint{
Key: "foo_1",
Effect: v1.TaintEffectNoSchedule,
},
expectedTaints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: false,
},
{
name: "remove taint successfully",
node: &v1.Node{
Spec: v1.NodeSpec{
Taints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
},
},
taintToRemove: &v1.Taint{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
expectedTaints: []v1.Taint{},
expectedResult: true,
},
{
name: "remove taint from node with no taint",
node: &v1.Node{
Spec: v1.NodeSpec{
Taints: []v1.Taint{},
},
},
taintToRemove: &v1.Taint{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
expectedTaints: []v1.Taint{},
expectedResult: false,
},
}
for _, c := range cases {
newNode, result, err := RemoveTaint(c.node, c.taintToRemove)
if err != nil {
t.Errorf("[%s] should not raise error but got: %v", c.name, err)
}
if result != c.expectedResult {
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
}
if !reflect.DeepEqual(newNode.Spec.Taints, c.expectedTaints) {
t.Errorf("[%s] the new node object should have taints %v, but got: %v", c.name, c.expectedTaints, newNode.Spec.Taints)
}
}
}
func TestDeleteTaint(t *testing.T) {
cases := []struct {
name string
taints []v1.Taint
taintToDelete *v1.Taint
expectedTaints []v1.Taint
expectedResult bool
}{
{
name: "delete taint with different name",
taints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
taintToDelete: &v1.Taint{Key: "foo_1", Effect: v1.TaintEffectNoSchedule},
expectedTaints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: false,
},
{
name: "delete taint with different effect",
taints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoExecute},
expectedTaints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: false,
},
{
name: "delete taint successfully",
taints: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule},
expectedTaints: []v1.Taint{},
expectedResult: true,
},
{
name: "delete taint from empty taint array",
taints: []v1.Taint{},
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule},
expectedTaints: []v1.Taint{},
expectedResult: false,
},
}
for _, c := range cases {
taints, result := DeleteTaint(c.taints, c.taintToDelete)
if result != c.expectedResult {
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
}
if !reflect.DeepEqual(taints, c.expectedTaints) {
t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints)
}
}
}
func TestDeleteTaintByKey(t *testing.T) {
cases := []struct {
name string
taints []v1.Taint
taintKey string
expectedTaints []v1.Taint
expectedResult bool
}{
{
name: "delete taint unsuccessfully",
taints: []v1.Taint{
{
Key: "foo",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
taintKey: "foo_1",
expectedTaints: []v1.Taint{
{
Key: "foo",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: false,
},
{
name: "delete taint successfully",
taints: []v1.Taint{
{
Key: "foo",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
taintKey: "foo",
expectedTaints: []v1.Taint{},
expectedResult: true,
},
{
name: "delete taint from empty taint array",
taints: []v1.Taint{},
taintKey: "foo",
expectedTaints: []v1.Taint{},
expectedResult: false,
},
}
for _, c := range cases {
taints, result := DeleteTaintsByKey(c.taints, c.taintKey)
if result != c.expectedResult {
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
}
if !reflect.DeepEqual(c.expectedTaints, taints) {
t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints)
}
}
}
func TestCheckIfTaintsAlreadyExists(t *testing.T) {
oldTaints := []v1.Taint{
{
Key: "foo_1",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "foo_2",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "foo_3",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
}
cases := []struct {
name string
taintsToCheck []v1.Taint
expectedResult string
}{
{
name: "empty array",
taintsToCheck: []v1.Taint{},
expectedResult: "",
},
{
name: "no match",
taintsToCheck: []v1.Taint{
{
Key: "foo_1",
Effect: v1.TaintEffectNoExecute,
},
},
expectedResult: "",
},
{
name: "match one taint",
taintsToCheck: []v1.Taint{
{
Key: "foo_2",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: "foo_2",
},
{
name: "match two taints",
taintsToCheck: []v1.Taint{
{
Key: "foo_2",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "foo_3",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedResult: "foo_2,foo_3",
},
}
for _, c := range cases {
result := CheckIfTaintsAlreadyExists(oldTaints, c.taintsToCheck)
if result != c.expectedResult {
t.Errorf("[%s] should return '%s', but got: '%s'", c.name, c.expectedResult, result)
}
}
}
func TestReorganizeTaints(t *testing.T) {
node := &v1.Node{
Spec: v1.NodeSpec{
Taints: []v1.Taint{
{
Key: "foo",
Value: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
},
}
cases := []struct {
name string
overwrite bool
taintsToAdd []v1.Taint
taintsToDelete []v1.Taint
expectedTaints []v1.Taint
expectedOperation string
expectedErr bool
}{
{
name: "no changes with overwrite is true",
overwrite: true,
taintsToAdd: []v1.Taint{},
taintsToDelete: []v1.Taint{},
expectedTaints: node.Spec.Taints,
expectedOperation: MODIFIED,
expectedErr: false,
},
{
name: "no changes with overwrite is false",
overwrite: false,
taintsToAdd: []v1.Taint{},
taintsToDelete: []v1.Taint{},
expectedTaints: node.Spec.Taints,
expectedOperation: UNTAINTED,
expectedErr: false,
},
{
name: "add new taint",
overwrite: false,
taintsToAdd: []v1.Taint{
{
Key: "foo_1",
Effect: v1.TaintEffectNoExecute,
},
},
taintsToDelete: []v1.Taint{},
expectedTaints: append([]v1.Taint{{Key: "foo_1", Effect: v1.TaintEffectNoExecute}}, node.Spec.Taints...),
expectedOperation: TAINTED,
expectedErr: false,
},
{
name: "delete taint with effect",
overwrite: false,
taintsToAdd: []v1.Taint{},
taintsToDelete: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedTaints: []v1.Taint{},
expectedOperation: UNTAINTED,
expectedErr: false,
},
{
name: "delete taint with no effect",
overwrite: false,
taintsToAdd: []v1.Taint{},
taintsToDelete: []v1.Taint{
{
Key: "foo",
},
},
expectedTaints: []v1.Taint{},
expectedOperation: UNTAINTED,
expectedErr: false,
},
{
name: "delete non-exist taint",
overwrite: false,
taintsToAdd: []v1.Taint{},
taintsToDelete: []v1.Taint{
{
Key: "foo_1",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedTaints: node.Spec.Taints,
expectedOperation: UNTAINTED,
expectedErr: true,
},
{
name: "add new taint and delete old one",
overwrite: false,
taintsToAdd: []v1.Taint{
{
Key: "foo_1",
Effect: v1.TaintEffectNoSchedule,
},
},
taintsToDelete: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedTaints: []v1.Taint{
{
Key: "foo_1",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedOperation: MODIFIED,
expectedErr: false,
},
}
for _, c := range cases {
operation, taints, err := ReorganizeTaints(node, c.overwrite, c.taintsToAdd, c.taintsToDelete)
if c.expectedErr && err == nil {
t.Errorf("[%s] expect to see an error, but did not get one", c.name)
} else if !c.expectedErr && err != nil {
t.Errorf("[%s] expect not to see an error, but got one: %v", c.name, err)
}
if !reflect.DeepEqual(c.expectedTaints, taints) {
t.Errorf("[%s] expect to see taint list %#v, but got: %#v", c.name, c.expectedTaints, taints)
}
if c.expectedOperation != operation {
t.Errorf("[%s] expect to see operation %s, but got: %s", c.name, c.expectedOperation, operation)
}
}
}
func TestParseTaints(t *testing.T) {
cases := []struct {
name string
spec []string
expectedTaints []v1.Taint
expectedTaintsToRemove []v1.Taint
expectedErr bool
}{
{
name: "invalid spec format",
spec: []string{"foo=abc"},
expectedErr: true,
},
{
name: "invalid spec effect for adding taint",
spec: []string{"foo=abc:invalid_effect"},
expectedErr: true,
},
{
name: "invalid spec effect for deleting taint",
spec: []string{"foo:invalid_effect-"},
expectedErr: true,
},
{
name: "add new taints",
spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule"},
expectedTaints: []v1.Taint{
{
Key: "foo",
Value: "abc",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "bar",
Value: "abc",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedErr: false,
},
{
name: "delete taints",
spec: []string{"foo:NoSchedule-", "bar:NoSchedule-"},
expectedTaintsToRemove: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedErr: false,
},
{
name: "add taints and delete taints",
spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule", "foo:NoSchedule-", "bar:NoSchedule-"},
expectedTaints: []v1.Taint{
{
Key: "foo",
Value: "abc",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "bar",
Value: "abc",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedTaintsToRemove: []v1.Taint{
{
Key: "foo",
Effect: v1.TaintEffectNoSchedule,
},
{
Key: "bar",
Effect: v1.TaintEffectNoSchedule,
},
},
expectedErr: false,
},
}
for _, c := range cases {
taints, taintsToRemove, err := ParseTaints(c.spec)
if c.expectedErr && err == nil {
t.Errorf("[%s] expected error, but got nothing", c.name)
}
if !c.expectedErr && err != nil {
t.Errorf("[%s] expected no error, but got: %v", c.name, err)
}
if !reflect.DeepEqual(c.expectedTaints, taints) {
t.Errorf("[%s] expected returen taints as %v, but got: %v", c.name, c.expectedTaints, taints)
}
if !reflect.DeepEqual(c.expectedTaintsToRemove, taintsToRemove) {
t.Errorf("[%s] expected return taints to be removed as %v, but got: %v", c.name, c.expectedTaintsToRemove, taintsToRemove)
}
}
}