hostutil_linux_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // +build linux
  2. /*
  3. Copyright 2014 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package hostutil
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "net"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "testing"
  23. "k8s.io/utils/exec"
  24. )
  25. func TestIsSharedSuccess(t *testing.T) {
  26. successMountInfo :=
  27. `62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  28. 76 62 8:1 / /boot rw,relatime shared:29 - ext4 /dev/sda1 rw,seclabel,data=ordered
  29. 78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel
  30. 80 62 0:42 / /var/lib/nfs/rpc_pipefs rw,relatime shared:31 - rpc_pipefs sunrpc rw
  31. 82 62 0:43 / /var/lib/foo rw,relatime shared:32 - tmpfs tmpfs rw
  32. 83 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw
  33. 227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  34. 224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  35. `
  36. tempDir, filename, err := writeFile(successMountInfo)
  37. if err != nil {
  38. t.Fatalf("cannot create temporary file: %v", err)
  39. }
  40. defer os.RemoveAll(tempDir)
  41. tests := []struct {
  42. name string
  43. path string
  44. expectedResult bool
  45. }{
  46. {
  47. // /var/lib/kubelet is a directory on mount '/' that is shared
  48. // This is the most common case.
  49. "shared",
  50. "/var/lib/kubelet",
  51. true,
  52. },
  53. {
  54. // 8a2a... is a directory on mount /var/lib/docker/devicemapper
  55. // that is private.
  56. "private",
  57. "/var/lib/docker/devicemapper/mnt/8a2a5c19eefb06d6f851dfcb240f8c113427f5b49b19658b5c60168e88267693/",
  58. false,
  59. },
  60. {
  61. // 'directory' is a directory on mount
  62. // /var/lib/docker/devicemapper/test/shared that is shared, but one
  63. // of its parent is private.
  64. "nested-shared",
  65. "/var/lib/docker/devicemapper/test/shared/my/test/directory",
  66. true,
  67. },
  68. {
  69. // /var/lib/foo is a mount point and it's shared
  70. "shared-mount",
  71. "/var/lib/foo",
  72. true,
  73. },
  74. {
  75. // /var/lib/bar is a mount point and it's private
  76. "private-mount",
  77. "/var/lib/bar",
  78. false,
  79. },
  80. }
  81. for _, test := range tests {
  82. ret, err := isShared(test.path, filename)
  83. if err != nil {
  84. t.Errorf("test %s got unexpected error: %v", test.name, err)
  85. }
  86. if ret != test.expectedResult {
  87. t.Errorf("test %s expected %v, got %v", test.name, test.expectedResult, ret)
  88. }
  89. }
  90. }
  91. func TestIsSharedFailure(t *testing.T) {
  92. errorTests := []struct {
  93. name string
  94. content string
  95. }{
  96. {
  97. // the first line is too short
  98. name: "too-short-line",
  99. content: `62 0 253:0 / / rw,relatime
  100. 76 62 8:1 / /boot rw,relatime shared:29 - ext4 /dev/sda1 rw,seclabel,data=ordered
  101. 78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel
  102. 80 62 0:42 / /var/lib/nfs/rpc_pipefs rw,relatime shared:31 - rpc_pipefs sunrpc rw
  103. 227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  104. 224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  105. `,
  106. },
  107. {
  108. // there is no root mount
  109. name: "no-root-mount",
  110. content: `76 62 8:1 / /boot rw,relatime shared:29 - ext4 /dev/sda1 rw,seclabel,data=ordered
  111. 78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel
  112. 80 62 0:42 / /var/lib/nfs/rpc_pipefs rw,relatime shared:31 - rpc_pipefs sunrpc rw
  113. 227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  114. 224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  115. `,
  116. },
  117. }
  118. for _, test := range errorTests {
  119. tempDir, filename, err := writeFile(test.content)
  120. if err != nil {
  121. t.Fatalf("cannot create temporary file: %v", err)
  122. }
  123. defer os.RemoveAll(tempDir)
  124. _, err = isShared("/", filename)
  125. if err == nil {
  126. t.Errorf("test %q: expected error, got none", test.name)
  127. }
  128. }
  129. }
  130. func TestGetSELinuxSupport(t *testing.T) {
  131. info :=
  132. `62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  133. 78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel
  134. 83 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw
  135. 227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
  136. 150 23 1:58 / /media/nfs_vol rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
  137. `
  138. tempDir, filename, err := writeFile(info)
  139. if err != nil {
  140. t.Fatalf("cannot create temporary file: %v", err)
  141. }
  142. defer os.RemoveAll(tempDir)
  143. tests := []struct {
  144. name string
  145. mountPoint string
  146. expectedResult bool
  147. }{
  148. {
  149. "ext4 on /",
  150. "/",
  151. true,
  152. },
  153. {
  154. "tmpfs on /var/lib/bar",
  155. "/var/lib/bar",
  156. false,
  157. },
  158. {
  159. "nfsv4",
  160. "/media/nfs_vol",
  161. false,
  162. },
  163. }
  164. for _, test := range tests {
  165. out, err := GetSELinux(test.mountPoint, filename)
  166. if err != nil {
  167. t.Errorf("Test %s failed with error: %s", test.name, err)
  168. }
  169. if test.expectedResult != out {
  170. t.Errorf("Test %s failed: expected %v, got %v", test.name, test.expectedResult, out)
  171. }
  172. }
  173. }
  174. func createSocketFile(socketDir string) (string, error) {
  175. testSocketFile := filepath.Join(socketDir, "mt.sock")
  176. // Switch to volume path and create the socket file
  177. // socket file can not have length of more than 108 character
  178. // and hence we must use relative path
  179. oldDir, _ := os.Getwd()
  180. err := os.Chdir(socketDir)
  181. if err != nil {
  182. return "", err
  183. }
  184. defer func() {
  185. os.Chdir(oldDir)
  186. }()
  187. _, socketCreateError := net.Listen("unix", "mt.sock")
  188. return testSocketFile, socketCreateError
  189. }
  190. func TestGetFileType(t *testing.T) {
  191. hu := NewHostUtil()
  192. testCase := []struct {
  193. name string
  194. expectedType FileType
  195. setUp func() (string, string, error)
  196. }{
  197. {
  198. "Directory Test",
  199. FileTypeDirectory,
  200. func() (string, string, error) {
  201. tempDir, err := ioutil.TempDir("", "test-get-filetype-")
  202. return tempDir, tempDir, err
  203. },
  204. },
  205. {
  206. "File Test",
  207. FileTypeFile,
  208. func() (string, string, error) {
  209. tempFile, err := ioutil.TempFile("", "test-get-filetype")
  210. if err != nil {
  211. return "", "", err
  212. }
  213. tempFile.Close()
  214. return tempFile.Name(), tempFile.Name(), nil
  215. },
  216. },
  217. {
  218. "Socket Test",
  219. FileTypeSocket,
  220. func() (string, string, error) {
  221. tempDir, err := ioutil.TempDir("", "test-get-filetype-")
  222. if err != nil {
  223. return "", "", err
  224. }
  225. tempSocketFile, err := createSocketFile(tempDir)
  226. return tempSocketFile, tempDir, err
  227. },
  228. },
  229. {
  230. "Block Device Test",
  231. FileTypeBlockDev,
  232. func() (string, string, error) {
  233. tempDir, err := ioutil.TempDir("", "test-get-filetype-")
  234. if err != nil {
  235. return "", "", err
  236. }
  237. tempBlockFile := filepath.Join(tempDir, "test_blk_dev")
  238. outputBytes, err := exec.New().Command("mknod", tempBlockFile, "b", "89", "1").CombinedOutput()
  239. if err != nil {
  240. err = fmt.Errorf("%v: %s ", err, outputBytes)
  241. }
  242. return tempBlockFile, tempDir, err
  243. },
  244. },
  245. {
  246. "Character Device Test",
  247. FileTypeCharDev,
  248. func() (string, string, error) {
  249. tempDir, err := ioutil.TempDir("", "test-get-filetype-")
  250. if err != nil {
  251. return "", "", err
  252. }
  253. tempCharFile := filepath.Join(tempDir, "test_char_dev")
  254. outputBytes, err := exec.New().Command("mknod", tempCharFile, "c", "89", "1").CombinedOutput()
  255. if err != nil {
  256. err = fmt.Errorf("%v: %s ", err, outputBytes)
  257. }
  258. return tempCharFile, tempDir, err
  259. },
  260. },
  261. }
  262. for idx, tc := range testCase {
  263. path, cleanUpPath, err := tc.setUp()
  264. if err != nil {
  265. // Locally passed, but upstream CI is not friendly to create such device files
  266. // Leave "Operation not permitted" out, which can be covered in an e2e test
  267. if isOperationNotPermittedError(err) {
  268. continue
  269. }
  270. t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
  271. }
  272. if len(cleanUpPath) > 0 {
  273. defer os.RemoveAll(cleanUpPath)
  274. }
  275. fileType, err := hu.GetFileType(path)
  276. if err != nil {
  277. t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
  278. }
  279. if fileType != tc.expectedType {
  280. t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType)
  281. }
  282. }
  283. }
  284. func isOperationNotPermittedError(err error) bool {
  285. if strings.Contains(err.Error(), "Operation not permitted") {
  286. return true
  287. }
  288. return false
  289. }
  290. func writeFile(content string) (string, string, error) {
  291. tempDir, err := ioutil.TempDir("", "mounter_shared_test")
  292. if err != nil {
  293. return "", "", err
  294. }
  295. filename := filepath.Join(tempDir, "mountinfo")
  296. err = ioutil.WriteFile(filename, []byte(content), 0600)
  297. if err != nil {
  298. os.RemoveAll(tempDir)
  299. return "", "", err
  300. }
  301. return tempDir, filename, nil
  302. }