]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[cloud] Add ability to delete old AMI images
authorMichael Brown <mcb30@ipxe.org>
Mon, 9 Sep 2024 14:00:24 +0000 (15:00 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 9 Sep 2024 14:02:27 +0000 (15:02 +0100)
Add the "--retain <N>" option to limit the number of retained old AMI
images (within the same family, architecture, and public visibility).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
contrib/cloud/aws-import

index 64b2996221fb01eb5ca671304fcf7541b31d2ca6..3b6b5223a0efc620317f467479b781c2f59f4118 100755 (executable)
@@ -47,7 +47,22 @@ def create_snapshot(region, description, image, tags):
     return snapshot_id
 
 
-def import_image(region, name, family, architecture, image, public, overwrite):
+def delete_images(region, filters, retain):
+    client = boto3.client('ec2', region_name=region)
+    resource = boto3.resource('ec2', region_name=region)
+    images = client.describe_images(Owners=['self'], Filters=filters)
+    old_images = sorted(images['Images'], key=lambda x: x['CreationDate'])
+    if retain > 0:
+        old_images = old_images[:-retain]
+    for image in old_images:
+        image_id = image['ImageId']
+        snapshot_id = image['BlockDeviceMappings'][0]['Ebs']['SnapshotId']
+        resource.Image(image_id).deregister()
+        resource.Snapshot(snapshot_id).delete()
+
+
+def import_image(region, name, family, architecture, image, public, overwrite,
+                 retain):
     """Import an AMI image"""
     client = boto3.client('ec2', region_name=region)
     resource = boto3.resource('ec2', region_name=region)
@@ -56,14 +71,16 @@ def import_image(region, name, family, architecture, image, public, overwrite):
         {'Key': 'family', 'Value': family},
         {'Key': 'architecture', 'Value': architecture},
     ]
-    images = client.describe_images(Filters=[{'Name': 'name',
-                                              'Values': [description]}])
-    if overwrite and images['Images']:
-        images = images['Images'][0]
-        image_id = images['ImageId']
-        snapshot_id = images['BlockDeviceMappings'][0]['Ebs']['SnapshotId']
-        resource.Image(image_id).deregister()
-        resource.Snapshot(snapshot_id).delete()
+    if overwrite:
+        filters = [{'Name': 'name', 'Values': [description]}]
+        delete_images(region=region, filters=filters, retain=0)
+    if retain is not None:
+        filters = [
+            {'Name': 'tag:family', 'Values': [family]},
+            {'Name': 'tag:architecture', 'Values': [architecture]},
+            {'Name': 'is-public', 'Values': [str(public).lower()]},
+        ]
+        delete_images(region=region, filters=filters, retain=retain)
     snapshot_id = create_snapshot(region=region, description=description,
                                   image=image, tags=tags)
     client.get_waiter('snapshot_completed').wait(SnapshotIds=[snapshot_id])
@@ -109,6 +126,8 @@ parser.add_argument('--public', '-p', action='store_true',
                     help="Make image public")
 parser.add_argument('--overwrite', action='store_true',
                     help="Overwrite any existing image with same name")
+parser.add_argument('--retain', type=int, metavar='NUM',
+                    help="Retain at most <NUM> old images")
 parser.add_argument('--region', '-r', action='append',
                     help="AWS region(s)")
 parser.add_argument('--wiki', '-w', metavar='FILE',
@@ -142,7 +161,8 @@ with ThreadPoolExecutor(max_workers=len(imports)) as executor:
                                architecture=architectures[image],
                                image=image,
                                public=args.public,
-                               overwrite=args.overwrite): (region, image)
+                               overwrite=args.overwrite,
+                               retain=args.retain): (region, image)
                for region, image in imports}
     results = {futures[future]: future.result()
                for future in as_completed(futures)}