github.com/secure-build/gitlab-runner@v12.5.0+incompatible/executors/docker/internal/volumes/cache_container.go (about) 1 package volumes 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 "github.com/docker/docker/api/types" 9 "github.com/docker/docker/api/types/container" 10 11 docker_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/docker" 12 ) 13 14 type containerClient interface { 15 docker_helpers.Client 16 17 LabelContainer(container *container.Config, containerType string, otherLabels ...string) 18 WaitForContainer(id string) error 19 RemoveContainer(ctx context.Context, id string) error 20 } 21 22 type CacheContainersManager interface { 23 FindOrCleanExisting(containerName string, containerPath string) string 24 Create(containerName string, containerPath string) (string, error) 25 Cleanup(ctx context.Context, ids []string) chan bool 26 } 27 28 type cacheContainerManager struct { 29 ctx context.Context 30 logger debugLogger 31 32 containerClient containerClient 33 34 helperImage *types.ImageInspect 35 failedContainerIDs []string 36 } 37 38 func NewCacheContainerManager(ctx context.Context, logger debugLogger, cClient containerClient, helperImage *types.ImageInspect) CacheContainersManager { 39 return &cacheContainerManager{ 40 ctx: ctx, 41 logger: logger, 42 containerClient: cClient, 43 helperImage: helperImage, 44 } 45 } 46 47 func (m *cacheContainerManager) FindOrCleanExisting(containerName string, containerPath string) string { 48 inspected, err := m.containerClient.ContainerInspect(m.ctx, containerName) 49 if err != nil { 50 m.logger.Debugln(fmt.Sprintf("Error while inspecting %q container: %v", containerName, err)) 51 return "" 52 } 53 54 // check if we have valid cache, if not remove the broken container 55 _, ok := inspected.Config.Volumes[containerPath] 56 if !ok { 57 m.logger.Debugln(fmt.Sprintf("Removing broken cache container for %q path", containerPath)) 58 err = m.containerClient.RemoveContainer(m.ctx, inspected.ID) 59 m.logger.Debugln(fmt.Sprintf("Cache container for %q path removed with %v", containerPath, err)) 60 61 return "" 62 } 63 64 return inspected.ID 65 } 66 67 func (m *cacheContainerManager) Create(containerName string, containerPath string) (string, error) { 68 containerID, err := m.createCacheContainer(containerName, containerPath) 69 if err != nil { 70 return "", err 71 } 72 73 err = m.startCacheContainer(containerID) 74 if err != nil { 75 return "", err 76 } 77 78 return containerID, nil 79 } 80 81 func (m *cacheContainerManager) createCacheContainer(containerName string, containerPath string) (string, error) { 82 config := &container.Config{ 83 Image: m.helperImage.ID, 84 Cmd: []string{"gitlab-runner-helper", "cache-init", containerPath}, 85 Volumes: map[string]struct{}{ 86 containerPath: {}, 87 }, 88 } 89 m.containerClient.LabelContainer(config, "cache", "cache.dir="+containerPath) 90 91 hostConfig := &container.HostConfig{ 92 LogConfig: container.LogConfig{ 93 Type: "json-file", 94 }, 95 } 96 97 resp, err := m.containerClient.ContainerCreate(m.ctx, config, hostConfig, nil, containerName) 98 if err != nil { 99 if resp.ID != "" { 100 m.failedContainerIDs = append(m.failedContainerIDs, resp.ID) 101 } 102 103 return "", err 104 } 105 106 return resp.ID, nil 107 } 108 109 func (m *cacheContainerManager) startCacheContainer(containerID string) error { 110 m.logger.Debugln(fmt.Sprintf("Starting cache container %q...", containerID)) 111 err := m.containerClient.ContainerStart(m.ctx, containerID, types.ContainerStartOptions{}) 112 if err != nil { 113 m.failedContainerIDs = append(m.failedContainerIDs, containerID) 114 115 return err 116 } 117 118 m.logger.Debugln(fmt.Sprintf("Waiting for cache container %q...", containerID)) 119 err = m.containerClient.WaitForContainer(containerID) 120 if err != nil { 121 m.failedContainerIDs = append(m.failedContainerIDs, containerID) 122 123 return err 124 } 125 126 return nil 127 } 128 129 func (m *cacheContainerManager) Cleanup(ctx context.Context, ids []string) chan bool { 130 done := make(chan bool, 1) 131 132 ids = append(m.failedContainerIDs, ids...) 133 134 go func() { 135 wg := new(sync.WaitGroup) 136 wg.Add(len(ids)) 137 for _, id := range ids { 138 m.remove(ctx, wg, id) 139 } 140 141 wg.Wait() 142 done <- true 143 }() 144 145 return done 146 } 147 148 func (m *cacheContainerManager) remove(ctx context.Context, wg *sync.WaitGroup, id string) { 149 go func() { 150 err := m.containerClient.RemoveContainer(ctx, id) 151 if err != nil { 152 m.logger.Debugln(fmt.Sprintf("Error while removing the container: %v", err)) 153 } 154 wg.Done() 155 }() 156 }