gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/executors/docker/internal/volumes/manager.go (about) 1 package volumes 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/volumes/parser" 9 ) 10 11 var ErrCacheVolumesDisabled = errors.New("cache volumes feature disabled") 12 13 type Manager interface { 14 Create(volume string) error 15 CreateTemporary(containerPath string) error 16 Binds() []string 17 ContainerIDs() []string 18 Cleanup(ctx context.Context) chan bool 19 } 20 21 type ManagerConfig struct { 22 CacheDir string 23 BaseContainerPath string 24 UniqueName string 25 DisableCache bool 26 } 27 28 type manager struct { 29 config ManagerConfig 30 logger debugLogger 31 parser parser.Parser 32 33 cacheContainersManager CacheContainersManager 34 35 volumeBindings []string 36 cacheContainerIDs []string 37 tmpContainerIDs []string 38 39 managedVolumes pathList 40 } 41 42 func NewManager(logger debugLogger, volumeParser parser.Parser, ccManager CacheContainersManager, config ManagerConfig) Manager { 43 return &manager{ 44 config: config, 45 logger: logger, 46 parser: volumeParser, 47 cacheContainersManager: ccManager, 48 volumeBindings: make([]string, 0), 49 cacheContainerIDs: make([]string, 0), 50 tmpContainerIDs: make([]string, 0), 51 managedVolumes: pathList{}, 52 } 53 } 54 55 func (m *manager) Create(volume string) error { 56 if len(volume) < 1 { 57 return nil 58 } 59 60 parsedVolume, err := m.parser.ParseVolume(volume) 61 if err != nil { 62 return err 63 } 64 65 switch parsedVolume.Len() { 66 case 2: 67 err = m.addHostVolume(parsedVolume) 68 case 1: 69 err = m.addCacheVolume(parsedVolume) 70 } 71 72 return err 73 } 74 75 func (m *manager) addHostVolume(volume *parser.Volume) error { 76 var err error 77 78 volume.Destination, err = m.getAbsoluteContainerPath(volume.Destination) 79 if err != nil { 80 return err 81 } 82 83 err = m.managedVolumes.Add(volume.Destination) 84 if err != nil { 85 return err 86 } 87 88 m.appendVolumeBind(volume) 89 90 return nil 91 } 92 93 func (m *manager) getAbsoluteContainerPath(dir string) (string, error) { 94 if m.parser.Path().IsRoot(dir) { 95 return "", errDirectoryIsRootPath 96 } 97 98 if m.parser.Path().IsAbs(dir) { 99 return dir, nil 100 } 101 102 return m.parser.Path().Join(m.config.BaseContainerPath, dir), nil 103 } 104 105 func (m *manager) appendVolumeBind(volume *parser.Volume) { 106 m.logger.Debugln(fmt.Sprintf("Using host-based %q for %q...", volume.Source, volume.Destination)) 107 108 m.volumeBindings = append(m.volumeBindings, volume.Definition()) 109 } 110 111 func (m *manager) addCacheVolume(volume *parser.Volume) error { 112 // disable cache for automatic container cache, 113 // but leave it for host volumes (they are shared on purpose) 114 if m.config.DisableCache { 115 m.logger.Debugln("Cache containers feature is disabled") 116 117 return ErrCacheVolumesDisabled 118 } 119 120 if m.config.CacheDir != "" { 121 return m.createHostBasedCacheVolume(volume.Destination) 122 } 123 124 _, err := m.createContainerBasedCacheVolume(volume.Destination) 125 126 return err 127 } 128 129 func (m *manager) createHostBasedCacheVolume(containerPath string) error { 130 var err error 131 132 containerPath, err = m.getAbsoluteContainerPath(containerPath) 133 if err != nil { 134 return err 135 } 136 137 err = m.managedVolumes.Add(containerPath) 138 if err != nil { 139 return err 140 } 141 142 hostPath := m.parser.Path().Join(m.config.CacheDir, m.config.UniqueName, hashContainerPath(containerPath)) 143 144 m.appendVolumeBind(&parser.Volume{ 145 Source: hostPath, 146 Destination: containerPath, 147 }) 148 149 return nil 150 } 151 152 func (m *manager) createContainerBasedCacheVolume(containerPath string) (string, error) { 153 containerPath, err := m.getAbsoluteContainerPath(containerPath) 154 if err != nil { 155 return "", err 156 } 157 158 err = m.managedVolumes.Add(containerPath) 159 if err != nil { 160 return "", err 161 } 162 163 containerName := fmt.Sprintf("%s-cache-%s", m.config.UniqueName, hashContainerPath(containerPath)) 164 containerID := m.cacheContainersManager.FindOrCleanExisting(containerName, containerPath) 165 166 // create new cache container for that project 167 if containerID == "" { 168 var err error 169 170 containerID, err = m.cacheContainersManager.Create(containerName, containerPath) 171 if err != nil { 172 return "", err 173 } 174 } 175 176 m.logger.Debugln(fmt.Sprintf("Using container %q as cache %q...", containerID, containerPath)) 177 m.cacheContainerIDs = append(m.cacheContainerIDs, containerID) 178 179 return containerID, nil 180 } 181 182 func (m *manager) CreateTemporary(containerPath string) error { 183 id, err := m.createContainerBasedCacheVolume(containerPath) 184 if err != nil { 185 return err 186 } 187 188 m.tmpContainerIDs = append(m.tmpContainerIDs, id) 189 190 return nil 191 } 192 193 func (m *manager) Binds() []string { 194 return m.volumeBindings 195 } 196 197 func (m *manager) ContainerIDs() []string { 198 return m.cacheContainerIDs 199 } 200 201 func (m *manager) Cleanup(ctx context.Context) chan bool { 202 return m.cacheContainersManager.Cleanup(ctx, m.tmpContainerIDs) 203 }