post_overlay_hostpath.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package disk
  15. import (
  16. "path/filepath"
  17. "yunion.io/x/pkg/errors"
  18. "yunion.io/x/onecloud/pkg/apis"
  19. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  20. "yunion.io/x/onecloud/pkg/hostman/container/volume_mount"
  21. fileutils "yunion.io/x/onecloud/pkg/util/fileutils2"
  22. "yunion.io/x/onecloud/pkg/util/mountutils"
  23. "yunion.io/x/onecloud/pkg/util/procutils"
  24. )
  25. func init() {
  26. registerPostOverlayDriver(newPostOverlayHostPath())
  27. }
  28. func newPostOverlayHostPath() iDiskPostOverlayDriver {
  29. return &postOverlayHostPath{}
  30. }
  31. type postOverlayHostPath struct {
  32. }
  33. func (p postOverlayHostPath) GetType() apis.ContainerVolumeMountDiskPostOverlayType {
  34. return apis.CONTAINER_VOLUME_MOUNT_DISK_POST_OVERLAY_HOSTPATH
  35. }
  36. func (p postOverlayHostPath) getSingleFilePath(ov *apis.ContainerVolumeMountDiskPostOverlay) string {
  37. if len(ov.HostLowerDir) == 1 && fileutils.IsFile(ov.HostLowerDir[0]) {
  38. return ov.HostLowerDir[0]
  39. }
  40. return ""
  41. }
  42. func (p postOverlayHostPath) mountDir(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) error {
  43. upperDir, err := d.getPostOverlayUpperDir(pod, ctrId, vm, ov, true)
  44. if err != nil {
  45. return errors.Wrapf(err, "get post overlay upper dir for container %s", ctrId)
  46. }
  47. workDir, err := d.getPostOverlayWorkDir(pod, ctrId, vm, ov, true)
  48. if err != nil {
  49. return errors.Wrapf(err, "get post overlay work dir for container %s", ctrId)
  50. }
  51. mergedDir, err := d.getPostOverlayMountpoint(pod, ctrId, vm, ov, true)
  52. if err != nil {
  53. return errors.Wrapf(err, "get post overlay mountpoint for container %s", ctrId)
  54. }
  55. if err := mountutils.MountOverlayWithFeatures(ov.HostLowerDir, upperDir, workDir, mergedDir, &mountutils.MountOverlayFeatures{
  56. MetaCopy: true,
  57. }); err != nil {
  58. return errors.Wrapf(err, "mount overlay dir for container %s", ctrId)
  59. }
  60. if err := volume_mount.ChangeDirOwnerDirectly(mergedDir, ov.FsUser, ov.FsGroup); err != nil {
  61. return errors.Wrapf(err, "change dir owner")
  62. }
  63. return nil
  64. }
  65. func (p postOverlayHostPath) getSingleFileMergedFilePath(mergedDir string, singleFilePath string) string {
  66. return filepath.Join(mergedDir, filepath.Base(singleFilePath))
  67. }
  68. func (p postOverlayHostPath) getSingleFileLowerDirFilePath(lowerDir string, singleFilePath string) string {
  69. return filepath.Join(lowerDir, filepath.Base(singleFilePath))
  70. }
  71. func (p postOverlayHostPath) mountSingleFile(singleFilePath string, d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) error {
  72. /*
  73. // 测试发现,如果 lowerDir 存在 bind mount 的文件,merge 后的目录中该文件为空,不是 bind mount 的 source 文件
  74. // 如果是单文件,创建一个目录,再把该文件 mount bind 过去到 lowerDir 里面
  75. lowerDir, err := d.getPostOverlayLowerDir(pod, ctrId, vm, ov, true)
  76. if err != nil {
  77. return errors.Wrapf(err, "get single file lower dir for container %s", ctrId)
  78. }
  79. lowerDirFilePath := p.getSingleFileLowerDirFilePath(lowerDir, singleFilePath)
  80. if !fileutils.Exists(lowerDirFilePath) {
  81. if err := volume_mount.TouchFile(lowerDirFilePath); err != nil {
  82. return errors.Wrapf(err, "touch file %s", lowerDirFilePath)
  83. }
  84. }
  85. if err := mountutils.MountBind(singleFilePath, lowerDirFilePath); err != nil {
  86. return errors.Wrapf(err, "mount bind %s to %s", singleFilePath, lowerDirFilePath)
  87. }
  88. */
  89. // 单文件,直接把该文件的目录作为 lowerDir
  90. lowerDir := filepath.Dir(singleFilePath)
  91. // 把单文件的 lowerDir 挂载到 targetMergedDir,然后在把 singleFileMergedFilePath bind mount 到 mergedDir(mergedDir 其实是个文件路径)
  92. upperDir, err := d.getPostOverlayUpperDir(pod, ctrId, vm, ov, true)
  93. if err != nil {
  94. return errors.Wrapf(err, "get post overlay upper dir for container %s", ctrId)
  95. }
  96. workDir, err := d.getPostOverlayWorkDir(pod, ctrId, vm, ov, true)
  97. if err != nil {
  98. return errors.Wrapf(err, "get post overlay work dir for container %s", ctrId)
  99. }
  100. mergedDst, err := d.getPostOverlayMountpoint(pod, ctrId, vm, ov, false)
  101. if err != nil {
  102. return errors.Wrapf(err, "get post overlay mountpoint for container %s", ctrId)
  103. }
  104. targetMergedDir, err := d.getPostOverlayMergedDir(pod, ctrId, vm, ov, true)
  105. if err != nil {
  106. return errors.Wrapf(err, "get post overlay merged dir for container %s", ctrId)
  107. }
  108. if err := mountutils.MountOverlayWithFeatures([]string{lowerDir}, upperDir, workDir, targetMergedDir, &mountutils.MountOverlayFeatures{
  109. MetaCopy: true,
  110. }); err != nil {
  111. return errors.Wrapf(err, "mount overlay dir for container %s", ctrId)
  112. }
  113. singleFileMergedFilePath := p.getSingleFileMergedFilePath(targetMergedDir, singleFilePath)
  114. if err := volume_mount.ChangeDirOwnerDirectly(singleFileMergedFilePath, ov.FsUser, ov.FsGroup); err != nil {
  115. return errors.Wrapf(err, "change file %s owner", singleFilePath)
  116. }
  117. if !fileutils.Exists(mergedDst) {
  118. if err := volume_mount.EnsureDir(filepath.Dir(mergedDst)); err != nil {
  119. return errors.Wrapf(err, "ensure dir %s", filepath.Dir(mergedDst))
  120. }
  121. if err := volume_mount.ChangeDirOwnerDirectly(filepath.Dir(mergedDst), ov.FsUser, ov.FsGroup); err != nil {
  122. return errors.Wrapf(err, "change dir %s owner", filepath.Dir(mergedDst))
  123. }
  124. if err := volume_mount.TouchFile(mergedDst); err != nil {
  125. return errors.Wrapf(err, "touch file %s", mergedDst)
  126. }
  127. if err := volume_mount.ChangeDirOwnerDirectly(mergedDst, ov.FsUser, ov.FsGroup); err != nil {
  128. return errors.Wrapf(err, "change file %s owner", mergedDst)
  129. }
  130. }
  131. if err := mountutils.MountBind(singleFileMergedFilePath, mergedDst); err != nil {
  132. return errors.Wrapf(err, "bind mount %s to %s", singleFileMergedFilePath, mergedDst)
  133. }
  134. return nil
  135. }
  136. func (p postOverlayHostPath) Mount(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) error {
  137. // 支持单文件挂载
  138. singleFilePath := ""
  139. if len(ov.HostLowerDir) == 1 && fileutils.IsFile(ov.HostLowerDir[0]) {
  140. singleFilePath = ov.HostLowerDir[0]
  141. }
  142. if singleFilePath != "" {
  143. return p.mountSingleFile(singleFilePath, d, pod, ctrId, vm, ov)
  144. } else {
  145. return p.mountDir(d, pod, ctrId, vm, ov)
  146. }
  147. }
  148. func (p postOverlayHostPath) unmountDir(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, cleanLayers bool) error {
  149. mergedDir, err := d.getPostOverlayMountpoint(pod, ctrId, vm, ov, false)
  150. if err != nil {
  151. return errors.Wrapf(err, "get post overlay mountpoint for container %s", ctrId)
  152. }
  153. if err := mountutils.Unmount(mergedDir, useLazy); err != nil {
  154. return errors.Wrapf(err, "unmount %s", mergedDir)
  155. }
  156. if cleanLayers {
  157. upperDir, err := d.getPostOverlayUpperDir(pod, ctrId, vm, ov, false)
  158. if err != nil {
  159. return errors.Wrapf(err, "get post overlay upper dir for container %s", ctrId)
  160. }
  161. if err := volume_mount.RemoveDir(upperDir); err != nil {
  162. return errors.Wrap(err, "remove upper dir")
  163. }
  164. workDir, err := d.getPostOverlayWorkDir(pod, ctrId, vm, ov, false)
  165. if err != nil {
  166. return errors.Wrapf(err, "get post overlay work dir for container %s", ctrId)
  167. }
  168. if err := volume_mount.RemoveDir(workDir); err != nil {
  169. return errors.Wrap(err, "remove work dir")
  170. }
  171. }
  172. return nil
  173. }
  174. func (p postOverlayHostPath) unmountSingleFile(singleFilePath string, d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, cleanLayers bool) error {
  175. mergedFile, err := d.getPostOverlayMountpoint(pod, ctrId, vm, ov, false)
  176. if err != nil {
  177. return errors.Wrapf(err, "get post overlay mountpoint for container %s", ctrId)
  178. }
  179. targetMergedDir, err := d.getPostOverlayMergedDir(pod, ctrId, vm, ov, false)
  180. if err != nil {
  181. return errors.Wrapf(err, "get post overlay merged dir for container %s", ctrId)
  182. }
  183. singleFileMergedFilePath := p.getSingleFileMergedFilePath(targetMergedDir, singleFilePath)
  184. // 先 unbind mergedFile
  185. if err := mountutils.Unmount(mergedFile, false); err != nil {
  186. return errors.Wrapf(err, "unmount %s of single file %s", mergedFile, singleFileMergedFilePath)
  187. }
  188. // 如果 mergedFile 是空文件,则删除这个空文件,因为空文件很可能是挂载时创建的
  189. if procutils.IsEmptyFile(mergedFile) {
  190. if err := volume_mount.RemoveDir(mergedFile); err != nil {
  191. return errors.Wrapf(err, "remove empty file %s", mergedFile)
  192. }
  193. }
  194. if err := mountutils.Unmount(targetMergedDir, useLazy); err != nil {
  195. return errors.Wrapf(err, "unmount %s", targetMergedDir)
  196. }
  197. /*
  198. // 再 unbind lowerDir 里面的 singleFilePath
  199. lowerDir, err := d.getPostOverlayLowerDir(pod, ctrId, vm, ov, false)
  200. if err != nil {
  201. return errors.Wrapf(err, "get post overlay lower dir for container %s", ctrId)
  202. }
  203. lowerDirFilePath := p.getSingleFileLowerDirFilePath(lowerDir, singleFilePath)
  204. if err := mountutils.Unmount(lowerDirFilePath, false); err != nil {
  205. return errors.Wrapf(err, "unmount %s of single file %s", lowerDirFilePath, singleFileMergedFilePath)
  206. }
  207. */
  208. if cleanLayers {
  209. upperDir, err := d.getPostOverlayUpperDir(pod, ctrId, vm, ov, false)
  210. if err != nil {
  211. return errors.Wrapf(err, "get post overlay upper dir for container %s", ctrId)
  212. }
  213. workDir, err := d.getPostOverlayWorkDir(pod, ctrId, vm, ov, false)
  214. if err != nil {
  215. return errors.Wrapf(err, "get post overlay work dir for container %s", ctrId)
  216. }
  217. for _, dir := range []string{upperDir, workDir, targetMergedDir} {
  218. if err := volume_mount.RemoveDir(dir); err != nil {
  219. return errors.Wrapf(err, "remove dir %s", dir)
  220. }
  221. }
  222. }
  223. return nil
  224. }
  225. func (p postOverlayHostPath) Unmount(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, cleanLayers bool) error {
  226. // 支持单文件卸载
  227. singleFilePath := ""
  228. if len(ov.HostLowerDir) == 1 && fileutils.IsFile(ov.HostLowerDir[0]) {
  229. singleFilePath = ov.HostLowerDir[0]
  230. }
  231. if singleFilePath != "" {
  232. return p.unmountSingleFile(singleFilePath, d, pod, ctrId, vm, ov, useLazy, cleanLayers)
  233. } else {
  234. return p.unmountDir(d, pod, ctrId, vm, ov, useLazy, cleanLayers)
  235. }
  236. }