123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- /*
- 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 util
- import (
- "io/ioutil"
- "os"
- "runtime"
- "testing"
- v1 "k8s.io/api/core/v1"
- _ "k8s.io/kubernetes/pkg/apis/core/install"
- "reflect"
- "strings"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/kubernetes/pkg/util/slice"
- "k8s.io/kubernetes/pkg/volume"
- )
- var nodeLabels = map[string]string{
- "test-key1": "test-value1",
- "test-key2": "test-value2",
- }
- func TestCheckVolumeNodeAffinity(t *testing.T) {
- type affinityTest struct {
- name string
- expectSuccess bool
- pv *v1.PersistentVolume
- }
- cases := []affinityTest{
- {
- name: "valid-nil",
- expectSuccess: true,
- pv: testVolumeWithNodeAffinity(t, nil),
- },
- {
- name: "valid-no-constraints",
- expectSuccess: true,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{}),
- },
- {
- name: "select-nothing",
- expectSuccess: false,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{Required: &v1.NodeSelector{}}),
- },
- {
- name: "select-nothing-empty-terms",
- expectSuccess: false,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{},
- },
- },
- },
- }),
- },
- {
- name: "valid-multiple-terms",
- expectSuccess: true,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key3",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value1", "test-value3"},
- },
- },
- },
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key2",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value0", "test-value2"},
- },
- },
- },
- },
- },
- }),
- },
- {
- name: "valid-multiple-match-expressions",
- expectSuccess: true,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key1",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value1", "test-value3"},
- },
- {
- Key: "test-key2",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value0", "test-value2"},
- },
- },
- },
- },
- },
- }),
- },
- {
- name: "invalid-multiple-match-expressions-key",
- expectSuccess: false,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key1",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value1", "test-value3"},
- },
- {
- Key: "test-key3",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value0", "test-value2"},
- },
- },
- },
- },
- },
- }),
- },
- {
- name: "invalid-multiple-match-expressions-values",
- expectSuccess: false,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key1",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value3", "test-value4"},
- },
- {
- Key: "test-key2",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value0", "test-value2"},
- },
- },
- },
- },
- },
- }),
- },
- {
- name: "invalid-multiple-terms",
- expectSuccess: false,
- pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
- Required: &v1.NodeSelector{
- NodeSelectorTerms: []v1.NodeSelectorTerm{
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key3",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value1", "test-value3"},
- },
- },
- },
- {
- MatchExpressions: []v1.NodeSelectorRequirement{
- {
- Key: "test-key2",
- Operator: v1.NodeSelectorOpIn,
- Values: []string{"test-value0", "test-value1"},
- },
- },
- },
- },
- },
- }),
- },
- }
- for _, c := range cases {
- err := CheckNodeAffinity(c.pv, nodeLabels)
- if err != nil && c.expectSuccess {
- t.Errorf("CheckTopology %v returned error: %v", c.name, err)
- }
- if err == nil && !c.expectSuccess {
- t.Errorf("CheckTopology %v returned success, expected error", c.name)
- }
- }
- }
- func testVolumeWithNodeAffinity(t *testing.T, affinity *v1.VolumeNodeAffinity) *v1.PersistentVolume {
- objMeta := metav1.ObjectMeta{Name: "test-constraints"}
- return &v1.PersistentVolume{
- ObjectMeta: objMeta,
- Spec: v1.PersistentVolumeSpec{
- NodeAffinity: affinity,
- },
- }
- }
- func TestLoadPodFromFile(t *testing.T) {
- tests := []struct {
- name string
- content string
- expectError bool
- }{
- {
- "yaml",
- `
- apiVersion: v1
- kind: Pod
- metadata:
- name: testpod
- spec:
- containers:
- - image: k8s.gcr.io/busybox
- `,
- false,
- },
- {
- "json",
- `
- {
- "apiVersion": "v1",
- "kind": "Pod",
- "metadata": {
- "name": "testpod"
- },
- "spec": {
- "containers": [
- {
- "image": "k8s.gcr.io/busybox"
- }
- ]
- }
- }`,
- false,
- },
- {
- "invalid pod",
- `
- apiVersion: v1
- kind: Pod
- metadata:
- name: testpod
- spec:
- - image: k8s.gcr.io/busybox
- `,
- true,
- },
- }
- for _, test := range tests {
- tempFile, err := ioutil.TempFile("", "podfile")
- defer os.Remove(tempFile.Name())
- if err != nil {
- t.Fatalf("cannot create temporary file: %v", err)
- }
- if _, err = tempFile.Write([]byte(test.content)); err != nil {
- t.Fatalf("cannot save temporary file: %v", err)
- }
- if err = tempFile.Close(); err != nil {
- t.Fatalf("cannot close temporary file: %v", err)
- }
- pod, err := LoadPodFromFile(tempFile.Name())
- if test.expectError {
- if err == nil {
- t.Errorf("test %q expected error, got nil", test.name)
- }
- } else {
- // no error expected
- if err != nil {
- t.Errorf("error loading pod %q: %v", test.name, err)
- }
- if pod == nil {
- t.Errorf("test %q expected pod, got nil", test.name)
- }
- }
- }
- }
- func TestCalculateTimeoutForVolume(t *testing.T) {
- pv := &v1.PersistentVolume{
- Spec: v1.PersistentVolumeSpec{
- Capacity: v1.ResourceList{
- v1.ResourceName(v1.ResourceStorage): resource.MustParse("500M"),
- },
- },
- }
- timeout := CalculateTimeoutForVolume(50, 30, pv)
- if timeout != 50 {
- t.Errorf("Expected 50 for timeout but got %v", timeout)
- }
- pv.Spec.Capacity[v1.ResourceStorage] = resource.MustParse("2Gi")
- timeout = CalculateTimeoutForVolume(50, 30, pv)
- if timeout != 60 {
- t.Errorf("Expected 60 for timeout but got %v", timeout)
- }
- pv.Spec.Capacity[v1.ResourceStorage] = resource.MustParse("150Gi")
- timeout = CalculateTimeoutForVolume(50, 30, pv)
- if timeout != 4500 {
- t.Errorf("Expected 4500 for timeout but got %v", timeout)
- }
- }
- func TestGenerateVolumeName(t *testing.T) {
- // Normal operation, no truncate
- v1 := GenerateVolumeName("kubernetes", "pv-cinder-abcde", 255)
- if v1 != "kubernetes-dynamic-pv-cinder-abcde" {
- t.Errorf("Expected kubernetes-dynamic-pv-cinder-abcde, got %s", v1)
- }
- // Truncate trailing "6789-dynamic"
- prefix := strings.Repeat("0123456789", 9) // 90 characters prefix + 8 chars. of "-dynamic"
- v2 := GenerateVolumeName(prefix, "pv-cinder-abcde", 100)
- expect := prefix[:84] + "-pv-cinder-abcde"
- if v2 != expect {
- t.Errorf("Expected %s, got %s", expect, v2)
- }
- // Truncate really long cluster name
- prefix = strings.Repeat("0123456789", 1000) // 10000 characters prefix
- v3 := GenerateVolumeName(prefix, "pv-cinder-abcde", 100)
- if v3 != expect {
- t.Errorf("Expected %s, got %s", expect, v3)
- }
- }
- func TestMountOptionFromSpec(t *testing.T) {
- scenarios := map[string]struct {
- volume *volume.Spec
- expectedMountList []string
- systemOptions []string
- }{
- "volume-with-mount-options": {
- volume: createVolumeSpecWithMountOption("good-mount-opts", "ro,nfsvers=3", v1.PersistentVolumeSpec{
- PersistentVolumeSource: v1.PersistentVolumeSource{
- NFS: &v1.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false},
- },
- }),
- expectedMountList: []string{"ro", "nfsvers=3"},
- systemOptions: nil,
- },
- "volume-with-bad-mount-options": {
- volume: createVolumeSpecWithMountOption("good-mount-opts", "", v1.PersistentVolumeSpec{
- PersistentVolumeSource: v1.PersistentVolumeSource{
- NFS: &v1.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false},
- },
- }),
- expectedMountList: []string{},
- systemOptions: nil,
- },
- "vol-with-sys-opts": {
- volume: createVolumeSpecWithMountOption("good-mount-opts", "ro,nfsvers=3", v1.PersistentVolumeSpec{
- PersistentVolumeSource: v1.PersistentVolumeSource{
- NFS: &v1.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false},
- },
- }),
- expectedMountList: []string{"ro", "nfsvers=3", "fsid=100", "hard"},
- systemOptions: []string{"fsid=100", "hard"},
- },
- "vol-with-sys-opts-with-dup": {
- volume: createVolumeSpecWithMountOption("good-mount-opts", "ro,nfsvers=3", v1.PersistentVolumeSpec{
- PersistentVolumeSource: v1.PersistentVolumeSource{
- NFS: &v1.NFSVolumeSource{Server: "localhost", Path: "/srv", ReadOnly: false},
- },
- }),
- expectedMountList: []string{"ro", "nfsvers=3", "fsid=100"},
- systemOptions: []string{"fsid=100", "ro"},
- },
- }
- for name, scenario := range scenarios {
- mountOptions := MountOptionFromSpec(scenario.volume, scenario.systemOptions...)
- if !reflect.DeepEqual(slice.SortStrings(mountOptions), slice.SortStrings(scenario.expectedMountList)) {
- t.Errorf("for %s expected mount options : %v got %v", name, scenario.expectedMountList, mountOptions)
- }
- }
- }
- func createVolumeSpecWithMountOption(name string, mountOptions string, spec v1.PersistentVolumeSpec) *volume.Spec {
- annotations := map[string]string{
- v1.MountOptionAnnotation: mountOptions,
- }
- objMeta := metav1.ObjectMeta{
- Name: name,
- Annotations: annotations,
- }
- pv := &v1.PersistentVolume{
- ObjectMeta: objMeta,
- Spec: spec,
- }
- return &volume.Spec{PersistentVolume: pv}
- }
- func TestGetWindowsPath(t *testing.T) {
- tests := []struct {
- path string
- expectedPath string
- }{
- {
- path: `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4/volumes/kubernetes.io~disk`,
- expectedPath: `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~disk`,
- },
- {
- path: `\var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~disk`,
- expectedPath: `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~disk`,
- },
- {
- path: `/`,
- expectedPath: `c:\`,
- },
- {
- path: ``,
- expectedPath: ``,
- },
- }
- for _, test := range tests {
- result := GetWindowsPath(test.path)
- if result != test.expectedPath {
- t.Errorf("GetWindowsPath(%v) returned (%v), want (%v)", test.path, result, test.expectedPath)
- }
- }
- }
- func TestIsWindowsUNCPath(t *testing.T) {
- tests := []struct {
- goos string
- path string
- isUNCPath bool
- }{
- {
- goos: "linux",
- path: `/usr/bin`,
- isUNCPath: false,
- },
- {
- goos: "linux",
- path: `\\.\pipe\foo`,
- isUNCPath: false,
- },
- {
- goos: "windows",
- path: `C:\foo`,
- isUNCPath: false,
- },
- {
- goos: "windows",
- path: `\\server\share\foo`,
- isUNCPath: true,
- },
- {
- goos: "windows",
- path: `\\?\server\share`,
- isUNCPath: true,
- },
- {
- goos: "windows",
- path: `\\?\c:\`,
- isUNCPath: true,
- },
- {
- goos: "windows",
- path: `\\.\pipe\valid_pipe`,
- isUNCPath: true,
- },
- }
- for _, test := range tests {
- result := IsWindowsUNCPath(test.goos, test.path)
- if result != test.isUNCPath {
- t.Errorf("IsWindowsUNCPath(%v) returned (%v), expected (%v)", test.path, result, test.isUNCPath)
- }
- }
- }
- func TestIsWindowsLocalPath(t *testing.T) {
- tests := []struct {
- goos string
- path string
- isWindowsLocalPath bool
- }{
- {
- goos: "linux",
- path: `/usr/bin`,
- isWindowsLocalPath: false,
- },
- {
- goos: "linux",
- path: `\\.\pipe\foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `C:\foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `:\foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `X:\foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `\\server\share\foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `\\?\server\share`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `\\?\c:\`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `\\.\pipe\valid_pipe`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `:foo`,
- isWindowsLocalPath: false,
- },
- {
- goos: "windows",
- path: `\foo`,
- isWindowsLocalPath: true,
- },
- {
- goos: "windows",
- path: `\foo\bar`,
- isWindowsLocalPath: true,
- },
- {
- goos: "windows",
- path: `/foo`,
- isWindowsLocalPath: true,
- },
- {
- goos: "windows",
- path: `/foo/bar`,
- isWindowsLocalPath: true,
- },
- }
- for _, test := range tests {
- result := IsWindowsLocalPath(test.goos, test.path)
- if result != test.isWindowsLocalPath {
- t.Errorf("isWindowsLocalPath(%v) returned (%v), expected (%v)", test.path, result, test.isWindowsLocalPath)
- }
- }
- }
- func TestMakeAbsolutePath(t *testing.T) {
- tests := []struct {
- goos string
- path string
- expectedPath string
- name string
- }{
- {
- goos: "linux",
- path: "non-absolute/path",
- expectedPath: "/non-absolute/path",
- name: "linux non-absolute path",
- },
- {
- goos: "linux",
- path: "/absolute/path",
- expectedPath: "/absolute/path",
- name: "linux absolute path",
- },
- {
- goos: "windows",
- path: "some\\path",
- expectedPath: "c:\\some\\path",
- name: "basic windows",
- },
- {
- goos: "windows",
- path: "/some/path",
- expectedPath: "c:/some/path",
- name: "linux path on windows",
- },
- {
- goos: "windows",
- path: "\\some\\path",
- expectedPath: "c:\\some\\path",
- name: "windows path no drive",
- },
- {
- goos: "windows",
- path: "\\:\\some\\path",
- expectedPath: "\\:\\some\\path",
- name: "windows path with colon",
- },
- }
- for _, test := range tests {
- if runtime.GOOS == test.goos {
- path := MakeAbsolutePath(test.goos, test.path)
- if path != test.expectedPath {
- t.Errorf("[%s] Expected %s saw %s", test.name, test.expectedPath, path)
- }
- }
- }
- }
|