Vacuum::getJobsToRemove()   B
last analyzed

Complexity

Conditions 6
Paths 18

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 34
rs 8.439
cc 6
eloc 20
nc 18
nop 2
1
<?php
2
3
namespace Joli\JoliCi;
4
5
use Docker\API\Model\ImageItem;
6
use Docker\Docker;
7
use Docker\Image;
8
use Joli\JoliCi\BuildStrategy\BuildStrategyInterface;
9
use Joli\JoliCi\Filesystem\Filesystem;
10
11
class Vacuum
12
{
13
    /**
14
     * @var \Docker\Docker Docker API Client
15
     */
16
    private $docker;
17
18
    /**
19
     * @var Naming Service use to create name for image and project
20
     */
21
    private $naming;
22
23
    /**
24
     * @var BuildStrategy\BuildStrategyInterface Strategy where we want to clean the builds
25
     */
26
    private $strategy;
27
28
    /**
29
     * @var string Location where the build are
30
     */
31
    private $buildPath;
32
33
    /**
34
     * @var \Joli\JoliCi\Filesystem\Filesystem
35
     */
36
    private $filesystem;
37
38
    /**
39
     * @param Docker                 $docker     Docker API Client
40
     * @param Naming                 $naming     Naming service
41
     * @param BuildStrategyInterface $strategy   Strategy used to create builds
42
     * @param Filesystem             $filesystem Filesystem service
43
     * @param string                 $buildPath  Directory where builds are created
44
     */
45
    public function __construct(Docker $docker, Naming $naming, BuildStrategyInterface $strategy, Filesystem $filesystem, $buildPath)
46
    {
47
        $this->docker     = $docker;
48
        $this->naming     = $naming;
49
        $this->strategy   = $strategy;
50
        $this->buildPath  = $buildPath;
51
        $this->filesystem = $filesystem;
52
    }
53
54
    /**
55
     * Clean containers, images and directory from a project
56
     *
57
     * @param string  $projectPath Location of the project
58
     * @param int     $keep        How many versions does we need to keep (1 is the default in order to have cache for each test)
59
     * @param boolean $force       Force removal for images
60
     */
61
    public function clean($projectPath, $keep = 1, $force = false)
62
    {
63
        $builds = $this->getJobsToRemove($projectPath, $keep);
64
65
        $this->cleanDirectories($builds);
66
        $this->cleanContainers($builds);
67
        $this->cleanImages($builds, $force);
68
    }
69
70
    /**
71
     * Clean directories for given builds
72
     *
73
     * @param \Joli\JoliCi\Job[] $jobs A list of jobs to remove images from
74
     */
75
    public function cleanDirectories($jobs = array())
76
    {
77
        foreach ($jobs as $job) {
78
            $this->filesystem->remove($job->getDirectory());
79
        }
80
    }
81
82
    /**
83
     * Clean images for given builds
84
     *
85
     * @param \Joli\JoliCi\Job[] $jobs A list of jobs to remove images from
86
     */
87
    public function cleanContainers($jobs = array())
88
    {
89
        $images     = array();
90
        $containers = array();
91
92
        foreach ($jobs as $job) {
93
            if (isset($job->getParameters()['image'])) {
94
                $images[] = $job->getParameters()['image'];
95
            } else {
96
                $images[] = sprintf('%s:%s', $job->getRepository(), $job->getTag());
97
            }
98
        }
99
100
        foreach ($this->docker->getContainerManager()->findAll(['all' => 1]) as $container) {
101
            $imageName = $container->getImage();
102
103
            foreach ($images as $image) {
104
                if ($image == $imageName) {
105
                    $containers[] = $container;
106
                }
107
108
                if (preg_match('#^'.$imageName.'#', $image->getId())) {
109
                    $containers[] = $container;
110
                }
111
            }
112
        }
113
114
        foreach ($containers as $container) {
115
            $this->docker->getContainerManager()->remove($container->getId(), [
116
                'v' => true,
117
                'force' => true
118
            ]);
119
        }
120
    }
121
122
    /**
123
     * Clean images for given builds
124
     *
125
     * @param \Joli\JoliCi\Job[] $jobs   A list of jobs to remove images from
126
     * @param boolean            $force  Force removal for images
127
     */
128
    public function cleanImages($jobs = array(), $force = false)
129
    {
130
        foreach ($jobs as $job) {
131
            $this->docker->getImageManager()->remove(sprintf('%s:%s', $job->getRepository(), $job->getTag()), [
132
                'force' => $force
133
            ]);
134
        }
135
    }
136
137
    /**
138
     * Get all jobs to remove given a project and how many versions to keep
139
     *
140
     * @param string $projectPath The project path
141
     * @param int    $keep        Number of project to keep
142
     *
143
     * @return \Joli\JoliCi\Job[] A list of jobs to remove
144
     */
145
    public function getJobsToRemove($projectPath, $keep = 1)
146
    {
147
        $currentJobs  = $this->strategy->getJobs($projectPath);
148
        $existingJobs = $this->getJobs($projectPath);
149
        $uniqList = array();
150
        $removes  = array();
151
        $ordered  = array();
152
153
        foreach ($currentJobs as $job) {
154
            $uniqList[] = $job->getUniq();
155
        }
156
157
        // Remove not existant image (old build configuration)
158
        foreach ($existingJobs as $job) {
159
            if (!in_array($job->getUniq(), $uniqList)) {
160
                $removes[] = $job;
161
            } else {
162
                $ordered[$job->getUniq()][$job->getCreated()->format('U')] = $job;
163
            }
164
        }
165
166
        // Remove old image
167
        foreach ($ordered as $jobs) {
168
            ksort($jobs);
169
            $keeped = count($jobs);
170
171
            while ($keeped > $keep) {
172
                $removes[] = array_shift($jobs);
173
                $keeped--;
174
            }
175
        }
176
177
        return $removes;
178
    }
179
180
    /**
181
     * Get all jobs related to a project
182
     *
183
     * @param string $projectPath Directory where the project is
184
     *
185
     * @return \Joli\JoliCi\Job[]
186
     */
187
    protected function getJobs($projectPath)
188
    {
189
        $jobs            = array();
190
        $project         = $this->naming->getProjectName($projectPath);
191
        $repositoryRegex = sprintf('#^%s_([a-z]+?)/%s:\d+-\d+$#', Job::BASE_NAME, $project);
192
193
        foreach ($this->docker->getImageManager()->findAll() as $image) {
194
            foreach ($image->getRepoTags() as $name) {
195
                if (preg_match($repositoryRegex, $name, $matches)) {
196
                    $jobs[] = $this->getJobFromImage($image, $name, $matches[1], $project);
197
                }
198
            }
199
        }
200
201
        return $jobs;
202
    }
203
204
    /**
205
     * Create a job from a docker image
206
     *
207
     * @param ImageItem $image
208
     * @param string    $strategy
209
     * @param string    $project
210
     *
211
     * @return \Joli\JoliCi\Job
212
     */
213
    protected function getJobFromImage(ImageItem $image, $imageName, $strategy, $project)
214
    {
215
        $tag = explode(':', $imageName)[1];
216
        list($uniq, $timestamp)     = explode('-', $tag);
217
218
        return new Job($project, $strategy, $uniq, array('image' => $image), "", \DateTime::createFromFormat('U', $timestamp));
219
    }
220
}
221