Browse Source

Support service names/IDs

master
Ricardo Branco 5 years ago
parent
commit
d660b1a512
1 changed files with 58 additions and 24 deletions
  1. 58
    24
      clean_registry.py

+ 58
- 24
clean_registry.py View File

@@ -5,13 +5,13 @@ It works on the whole registry or the specified repositories.
5 5
 The optional -x flag may be used to completely remove the specified repositories or tagged images.
6 6
 
7 7
 NOTES:
8
-  - This script stops the Registry container during cleanup to prevent corruption,
8
+  - This script pauses the Registry container during cleanup to prevent corruption,
9 9
     making it temporarily unavailable to clients.
10 10
   - This script assumes local storage (the filesystem storage driver).
11 11
   - This script may run stand-alone (on local setups) or dockerized (which supports remote Docker setups).
12 12
   - This script is Python 3 only.
13 13
 
14
-v1.3.2 by Ricardo Branco
14
+v1.4 by Ricardo Branco
15 15
 
16 16
 MIT License
17 17
 """
@@ -34,7 +34,7 @@ from docker.errors import APIError, NotFound, TLSParameterError
34 34
 
35 35
 import yaml
36 36
 
37
-VERSION = "1.3.2"
37
+VERSION = "1.4"
38 38
 REGISTRY_DIR = "REGISTRY_STORAGE_FILESYSTEM_ROOTREGISTRY_DIR"
39 39
 args = None
40 40
 
@@ -151,14 +151,14 @@ def check_name(image):
151 151
 
152 152
 class RegistryCleaner():
153 153
     '''Simple callable class for Docker Registry cleaning duties'''
154
-    def __init__(self, container=None, volume=None):
154
+    def __init__(self, container_or_service=None, volume=None):
155 155
         try:
156 156
             self.docker = docker.from_env()
157 157
         except TLSParameterError as err:
158 158
             error(err)
159 159
 
160
-        if container is None:
161
-            self.container = None
160
+        if volume:
161
+            self.containers = None
162 162
             try:
163 163
                 self.volume = self.docker.volumes.get(volume)
164 164
                 self.registry_dir = self.volume.attrs['Mountpoint']
@@ -171,14 +171,10 @@ class RegistryCleaner():
171 171
                     self.registry_dir = "/var/lib/registry"
172 172
             return
173 173
 
174
-        try:
175
-            self.info = self.docker.api.inspect_container(container)
176
-            self.container = self.info['Id']
177
-        except (APIError, exceptions.ConnectionError) as err:
178
-            error(err)
174
+        self._get_info(container_or_service)
179 175
 
180 176
         if not re.match("registry:2(@sha256:[0-9a-f]{64})?$", self.info['Config']['Image']):
181
-            error("The container %s is not running the registry:2 image" % (container))
177
+            error("%s is not running the registry:2 image" % (container_or_service))
182 178
 
183 179
         if LooseVersion(self.get_image_version()) < LooseVersion("v2.4.0"):
184 180
             error("You're not running Docker Registry 2.4.0+")
@@ -194,8 +190,11 @@ class RegistryCleaner():
194 190
         except FileNotFoundError as err:
195 191
             error(err)
196 192
 
197
-        if self.container is not None:
198
-            self.docker.api.stop(self.container)
193
+        # We could use stop() but the orchestrator would start another container
194
+        if self.containers is not None:
195
+            for container in self.containers:
196
+                if self.info['State']['Running']:
197
+                    self.docker.api.pause(container)
199 198
 
200 199
         images = args.images or \
201 200
             map(os.path.dirname, iglob("**/_manifests", recursive=True))
@@ -208,17 +207,52 @@ class RegistryCleaner():
208 207
         if not self.garbage_collect():
209 208
             exit_status = 1
210 209
 
211
-        if self.container is not None:
212
-            self.docker.api.start(self.container)
210
+        if self.containers is not None:
211
+            for container in self.containers:
212
+                if self.info['State']['Running']:
213
+                    self.docker.api.unpause(container)
213 214
 
214 215
         self.docker.close()
215 216
         return exit_status
216 217
 
218
+    def _get_info(self, container_or_service):
219
+        self.containers = []
220
+
221
+        # Is a service?
222
+        try:
223
+            service = self.docker.services.get(container_or_service)
224
+        except NotFound:
225
+            pass
226
+        except (APIError, exceptions.ConnectionError) as err:
227
+            error(err)
228
+        else:
229
+            # Get list of containers to pause them all
230
+            try:
231
+                tasks = service.tasks(filters={'desired-state': "running"})
232
+            except (APIError, NotFound, exceptions.ConnectionError) as err:
233
+                error(err)
234
+            self.containers = [
235
+                item['Status']['ContainerStatus']['ContainerID']
236
+                for _, item in enumerate(tasks)
237
+            ]
238
+
239
+        # Get information from the first container in list.
240
+        # We can't get the source of /var/lib/registry from inspect_service()
241
+        #   if a bind mount was not specified.
242
+        try:
243
+            self.info = self.docker.api.inspect_container(
244
+                self.containers[0] if self.containers else container_or_service
245
+            )
246
+        except (APIError, NotFound, exceptions.ConnectionError) as err:
247
+            error(err)
248
+        if not self.containers:
249
+            self.containers = [self.info['Id']]
250
+
217 251
     def get_file(self, path):
218 252
         '''Returns the contents of the specified file from the container'''
219 253
         try:
220 254
             with BytesIO(b"".join(
221
-                    _ for _ in self.docker.api.get_archive(self.container, path)[0]
255
+                    _ for _ in self.docker.api.get_archive(self.containers[0], path)[0]
222 256
             )) as buf, tarfile.open(fileobj=buf) \
223 257
                     as tar, tar.extractfile(os.path.basename(path)) \
224 258
                     as infile:
@@ -263,11 +297,11 @@ class RegistryCleaner():
263 297
         '''Gets the Docker distribution version running on the container'''
264 298
         if self.info['State']['Running']:
265 299
             data = self.docker.containers.get(
266
-                self.container
300
+                self.containers[0]
267 301
             ).exec_run("/bin/registry --version").output
268 302
         else:
269 303
             data = self.docker.containers.run(
270
-                self.info["Image"], command="--version", remove=True
304
+                self.info['Config']['Image'], command="--version", remove=True
271 305
             )
272 306
         return data.decode('utf-8').split()[2]
273 307
 
@@ -308,7 +342,7 @@ class RegistryCleaner():
308 342
 def main():
309 343
     '''Main function'''
310 344
     progname = os.path.basename(sys.argv[0])
311
-    usage = "\rUsage: " + progname + " [OPTIONS] VOLUME|CONTAINER [REPOSITORY[:TAG]]..." + """
345
+    usage = "\rUsage: " + progname + " [OPTIONS] SERVICE|CONTAINER|VOLUME [REPOSITORY[:TAG]]..." + """
312 346
 Options:
313 347
         -x, --remove    Remove the specified images or repositories.
314 348
         -v, --volume    Specify a volume instead of container.
@@ -321,7 +355,7 @@ Options:
321 355
     parser.add_argument('-x', '--remove', action='store_true')
322 356
     parser.add_argument('-v', '--volume', action='store_true')
323 357
     parser.add_argument('-V', '--version', action='store_true')
324
-    parser.add_argument('container_or_volume', nargs='?')
358
+    parser.add_argument('foobar', nargs='?')
325 359
     parser.add_argument('images', nargs='*')
326 360
     global args
327 361
     args = parser.parse_args()
@@ -332,7 +366,7 @@ Options:
332 366
     elif args.version:
333 367
         print(progname + " " + VERSION)
334 368
         sys.exit(0)
335
-    elif not args.container_or_volume:
369
+    elif not args.foobar:
336 370
         print('usage: ' + usage)
337 371
         sys.exit(1)
338 372
 
@@ -344,9 +378,9 @@ Options:
344 378
         error("The -x option requires that you specify at least one repository...")
345 379
 
346 380
     if args.volume:
347
-        cleaner = RegistryCleaner(volume=args.container_or_volume)
381
+        cleaner = RegistryCleaner(volume=args.foobar)
348 382
     else:
349
-        cleaner = RegistryCleaner(container=args.container_or_volume)
383
+        cleaner = RegistryCleaner(container_or_service=args.foobar)
350 384
 
351 385
     sys.exit(cleaner())
352 386
 

Loading…
Cancel
Save