post_overlay.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. "math"
  17. "path/filepath"
  18. "sort"
  19. "strings"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/onecloud/pkg/apis"
  24. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  25. "yunion.io/x/onecloud/pkg/hostman/container/volume_mount"
  26. )
  27. const (
  28. POST_OVERLAY_PREFIX_LOWER_DIR = "_post_overlay_lower_"
  29. POST_OVERLAY_PREFIX_WORK_DIR = "_post_overlay_work_"
  30. POST_OVERLAY_PREFIX_UPPER_DIR = "_post_overlay_upper_"
  31. POST_OVERLAY_PREFIX_MERGED_DIR = "_post_overlay_merged_"
  32. )
  33. func (d disk) MountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay) error {
  34. return d.newPostOverlay().mountPostOverlays(pod, ctrId, vm, ovs)
  35. }
  36. func (d disk) UnmountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, clearLayers bool) error {
  37. return d.newPostOverlay().unmountPostOverlays(pod, ctrId, vm, ovs, useLazy, clearLayers)
  38. }
  39. func (d disk) getPostOverlayRootPrefixDir(prefixDir string, pod volume_mount.IPodInfo, vm *hostapi.ContainerVolumeMount, ctrId string) (string, error) {
  40. hostPath, err := d.GetHostDiskRootPath(pod, vm)
  41. if err != nil {
  42. return "", errors.Wrap(err, "get host disk root path")
  43. }
  44. uniqDir := ctrId
  45. if vm.UniqueName != "" {
  46. uniqDir = vm.UniqueName
  47. }
  48. return filepath.Join(hostPath, prefixDir, uniqDir), nil
  49. }
  50. func (d disk) GetPostOverlayRootWorkDir(pod volume_mount.IPodInfo, vm *hostapi.ContainerVolumeMount, ctrId string) (string, error) {
  51. return d.getPostOverlayRootPrefixDir(POST_OVERLAY_PREFIX_WORK_DIR, pod, vm, ctrId)
  52. }
  53. func (d disk) GetPostOverlayRootUpperDir(pod volume_mount.IPodInfo, vm *hostapi.ContainerVolumeMount, ctrId string, pov *apis.ContainerVolumeMountDiskPostOverlay) (string, error) {
  54. if pov.Image != nil && pov.Image.UpperConfig != nil {
  55. config := pov.Image.UpperConfig
  56. if config.Disk.SubPath == "" {
  57. return "", errors.Errorf("sub_path of upper config is empty")
  58. }
  59. hostPath, err := d.GetHostDiskRootPath(pod, vm)
  60. if err != nil {
  61. return "", errors.Wrap(err, "get host disk root path")
  62. }
  63. upperDir := filepath.Join(hostPath, vm.Disk.SubDirectory, config.Disk.SubPath)
  64. return upperDir, nil
  65. }
  66. return d.getPostOverlayRootPrefixDir(POST_OVERLAY_PREFIX_UPPER_DIR, pod, vm, ctrId)
  67. }
  68. type iDiskPostOverlay interface {
  69. mountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay) error
  70. unmountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, clearLayers bool) error
  71. }
  72. type diskPostOverlay struct {
  73. disk disk
  74. }
  75. func newDiskPostOverlay(d disk) iDiskPostOverlay {
  76. return &diskPostOverlay{
  77. disk: d,
  78. }
  79. }
  80. var (
  81. postOverlayDrivers = make(map[apis.ContainerVolumeMountDiskPostOverlayType]iDiskPostOverlayDriver)
  82. )
  83. func registerPostOverlayDriver(drv iDiskPostOverlayDriver) {
  84. postOverlayDrivers[drv.GetType()] = drv
  85. }
  86. func getPostOverlayDriver(typ apis.ContainerVolumeMountDiskPostOverlayType) iDiskPostOverlayDriver {
  87. return postOverlayDrivers[typ]
  88. }
  89. type iDiskPostOverlayDriver interface {
  90. GetType() apis.ContainerVolumeMountDiskPostOverlayType
  91. Mount(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) error
  92. Unmount(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, clearLayers bool) error
  93. }
  94. func (d diskPostOverlay) getDriver(ov *apis.ContainerVolumeMountDiskPostOverlay) iDiskPostOverlayDriver {
  95. return getPostOverlayDriver(ov.GetType())
  96. }
  97. // getMinPathMapKeyLength 获取 PathMap 中最短 key 的长度
  98. // 如果 Image 为 nil 或 PathMap 为空,返回一个很大的值,确保排在后面
  99. func getMinPathMapKeyLength(ov *apis.ContainerVolumeMountDiskPostOverlay) int {
  100. if ov.Image == nil || len(ov.Image.PathMap) == 0 {
  101. return math.MaxInt // 返回一个很大的值,确保没有 PathMap 的排在后面
  102. }
  103. minLen := math.MaxInt
  104. for key := range ov.Image.PathMap {
  105. if len(key) < minLen {
  106. minLen = len(key)
  107. }
  108. }
  109. return minLen
  110. }
  111. func sortPostOverlayByPathMapLength(ovs []*apis.ContainerVolumeMountDiskPostOverlay) {
  112. sort.Slice(ovs, func(i, j int) bool {
  113. lenI := getMinPathMapKeyLength(ovs[i])
  114. lenJ := getMinPathMapKeyLength(ovs[j])
  115. return lenI < lenJ
  116. })
  117. }
  118. func (d diskPostOverlay) mountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay) error {
  119. // 根据 PathMap 中最短路径长度排序,路径短的排在前面
  120. sortPostOverlayByPathMapLength(ovs)
  121. for _, ov := range ovs {
  122. log.Debugf("mount container %s post overlay dir: %s", ctrId, jsonutils.Marshal(ov).PrettyString())
  123. if err := d.getDriver(ov).Mount(d, pod, ctrId, vm, ov); err != nil {
  124. return errors.Wrapf(err, "mount container %s post overlay dir: %#v", ctrId, ov)
  125. }
  126. }
  127. return nil
  128. }
  129. func (d diskPostOverlay) unmountPostOverlays(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ovs []*apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, clearLayers bool) error {
  130. //(与 mount 顺序相反)
  131. sortPostOverlayByPathMapLength(ovs)
  132. for i := len(ovs) - 1; i >= 0; i-- {
  133. ov := ovs[i]
  134. log.Debugf("-- unmount container %s post overlay dir: %s", ctrId, jsonutils.Marshal(ov).PrettyString())
  135. if err := d.getDriver(ov).Unmount(d, pod, ctrId, vm, ov, useLazy, clearLayers); err != nil {
  136. return errors.Wrapf(err, "unmount container %s post overlay dir: %#v", ctrId, ov)
  137. }
  138. }
  139. return nil
  140. }
  141. func (d diskPostOverlay) getPostOverlayDirWithPrefix(
  142. prefixDir string,
  143. pod volume_mount.IPodInfo, ctrId string,
  144. vm *hostapi.ContainerVolumeMount,
  145. ov *apis.ContainerVolumeMountDiskPostOverlay,
  146. ensure bool,
  147. ) (string, error) {
  148. rootPath, err := d.disk.getPostOverlayRootPrefixDir(prefixDir, pod, vm, ctrId)
  149. if err != nil {
  150. return "", errors.Wrap(err, "get post overlay root path")
  151. }
  152. workDir := filepath.Join(rootPath, ov.ContainerTargetDir)
  153. if ov.FlattenLayers {
  154. workDir = filepath.Join(rootPath, strings.ReplaceAll(ov.ContainerTargetDir, "/", "_"))
  155. }
  156. if ensure {
  157. if err := volume_mount.EnsureDir(workDir); err != nil {
  158. return "", errors.Wrapf(err, "makeDir %s", workDir)
  159. }
  160. }
  161. return workDir, nil
  162. }
  163. func (d diskPostOverlay) getPostOverlayWorkDir(
  164. pod volume_mount.IPodInfo, ctrId string,
  165. vm *hostapi.ContainerVolumeMount,
  166. ov *apis.ContainerVolumeMountDiskPostOverlay,
  167. ensure bool,
  168. ) (string, error) {
  169. return d.getPostOverlayDirWithPrefix(POST_OVERLAY_PREFIX_WORK_DIR, pod, ctrId, vm, ov, ensure)
  170. }
  171. func (d diskPostOverlay) getPostOverlayUpperDir(
  172. pod volume_mount.IPodInfo, ctrId string,
  173. vm *hostapi.ContainerVolumeMount,
  174. ov *apis.ContainerVolumeMountDiskPostOverlay,
  175. ensure bool,
  176. ) (string, error) {
  177. if ov.HostUpperDir != "" {
  178. return ov.HostUpperDir, nil
  179. }
  180. return d.getPostOverlayDirWithPrefix(POST_OVERLAY_PREFIX_UPPER_DIR, pod, ctrId, vm, ov, ensure)
  181. }
  182. func (d diskPostOverlay) getPostOverlayLowerDir(
  183. pod volume_mount.IPodInfo, ctrId string,
  184. vm *hostapi.ContainerVolumeMount,
  185. ov *apis.ContainerVolumeMountDiskPostOverlay,
  186. ensure bool,
  187. ) (string, error) {
  188. return d.getPostOverlayDirWithPrefix(POST_OVERLAY_PREFIX_LOWER_DIR, pod, ctrId, vm, ov, ensure)
  189. }
  190. func (d diskPostOverlay) getPostOverlayMergedDir(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, ensure bool) (string, error) {
  191. return d.getPostOverlayDirWithPrefix(POST_OVERLAY_PREFIX_MERGED_DIR, pod, ctrId, vm, ov, ensure)
  192. }
  193. func (d diskPostOverlay) getPostOverlayMountpoint(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, ensure bool) (string, error) {
  194. ctrMountHostPath, err := d.disk.GetRuntimeMountHostPath(pod, ctrId, vm)
  195. if err != nil {
  196. return "", errors.Wrap(err, "get runtime mount host_path")
  197. }
  198. // remove hostPath sub_directory path
  199. ctrMountHostPath = strings.TrimSuffix(ctrMountHostPath, vm.Disk.SubDirectory)
  200. mergedDir := filepath.Join(ctrMountHostPath, ov.ContainerTargetDir)
  201. if ensure {
  202. if err := volume_mount.EnsureDir(mergedDir); err != nil {
  203. return "", errors.Wrap(err, "make merged mountpoint dir")
  204. }
  205. }
  206. return mergedDir, nil
  207. }