##// END OF EJS Templates
automation: move image operations to own functions...
Gregory Szorc -
r42470:195dcc10 default
parent child Browse files
Show More
@@ -402,14 +402,14 b' def ensure_iam_state(iamclient, iamresou'
402 profile.add_role(RoleName=role)
402 profile.add_role(RoleName=role)
403
403
404
404
405 def find_windows_server_2019_image(ec2resource):
405 def find_image(ec2resource, owner_id, name):
406 """Find the Amazon published Windows Server 2019 base image."""
406 """Find an AMI by its owner ID and name."""
407
407
408 images = ec2resource.images.filter(
408 images = ec2resource.images.filter(
409 Filters=[
409 Filters=[
410 {
410 {
411 'Name': 'owner-alias',
411 'Name': 'owner-id',
412 'Values': ['amazon'],
412 'Values': [owner_id],
413 },
413 },
414 {
414 {
415 'Name': 'state',
415 'Name': 'state',
@@ -421,14 +421,14 b' def find_windows_server_2019_image(ec2re'
421 },
421 },
422 {
422 {
423 'Name': 'name',
423 'Name': 'name',
424 'Values': ['Windows_Server-2019-English-Full-Base-2019.02.13'],
424 'Values': [name],
425 },
425 },
426 ])
426 ])
427
427
428 for image in images:
428 for image in images:
429 return image
429 return image
430
430
431 raise Exception('unable to find Windows Server 2019 image')
431 raise Exception('unable to find image for %s' % name)
432
432
433
433
434 def ensure_security_groups(ec2resource, prefix='hg-'):
434 def ensure_security_groups(ec2resource, prefix='hg-'):
@@ -684,6 +684,84 b' def create_temp_windows_ec2_instances(c:'
684 yield instances
684 yield instances
685
685
686
686
687 def resolve_fingerprint(fingerprint):
688 fingerprint = json.dumps(fingerprint, sort_keys=True)
689 return hashlib.sha256(fingerprint.encode('utf-8')).hexdigest()
690
691
692 def find_and_reconcile_image(ec2resource, name, fingerprint):
693 """Attempt to find an existing EC2 AMI with a name and fingerprint.
694
695 If an image with the specified fingerprint is found, it is returned.
696 Otherwise None is returned.
697
698 Existing images for the specified name that don't have the specified
699 fingerprint or are missing required metadata or deleted.
700 """
701 # Find existing AMIs with this name and delete the ones that are invalid.
702 # Store a reference to a good image so it can be returned one the
703 # image state is reconciled.
704 images = ec2resource.images.filter(
705 Filters=[{'Name': 'name', 'Values': [name]}])
706
707 existing_image = None
708
709 for image in images:
710 if image.tags is None:
711 print('image %s for %s lacks required tags; removing' % (
712 image.id, image.name))
713 remove_ami(ec2resource, image)
714 else:
715 tags = {t['Key']: t['Value'] for t in image.tags}
716
717 if tags.get('HGIMAGEFINGERPRINT') == fingerprint:
718 existing_image = image
719 else:
720 print('image %s for %s has wrong fingerprint; removing' % (
721 image.id, image.name))
722 remove_ami(ec2resource, image)
723
724 return existing_image
725
726
727 def create_ami_from_instance(ec2client, instance, name, description,
728 fingerprint):
729 """Create an AMI from a running instance.
730
731 Returns the ``ec2resource.Image`` representing the created AMI.
732 """
733 instance.stop()
734
735 ec2client.get_waiter('instance_stopped').wait(
736 InstanceIds=[instance.id],
737 WaiterConfig={
738 'Delay': 5,
739 })
740 print('%s is stopped' % instance.id)
741
742 image = instance.create_image(
743 Name=name,
744 Description=description,
745 )
746
747 image.create_tags(Tags=[
748 {
749 'Key': 'HGIMAGEFINGERPRINT',
750 'Value': fingerprint,
751 },
752 ])
753
754 print('waiting for image %s' % image.id)
755
756 ec2client.get_waiter('image_available').wait(
757 ImageIds=[image.id],
758 )
759
760 print('image %s available as %s' % (image.id, image.name))
761
762 return image
763
764
687 def ensure_windows_dev_ami(c: AWSConnection, prefix='hg-'):
765 def ensure_windows_dev_ami(c: AWSConnection, prefix='hg-'):
688 """Ensure Windows Development AMI is available and up-to-date.
766 """Ensure Windows Development AMI is available and up-to-date.
689
767
@@ -702,6 +780,10 b' def ensure_windows_dev_ami(c: AWSConnect'
702
780
703 name = '%s%s' % (prefix, 'windows-dev')
781 name = '%s%s' % (prefix, 'windows-dev')
704
782
783 image = find_image(ec2resource,
784 '801119661308',
785 'Windows_Server-2019-English-Full-Base-2019.02.13')
786
705 config = {
787 config = {
706 'BlockDeviceMappings': [
788 'BlockDeviceMappings': [
707 {
789 {
@@ -713,7 +795,7 b' def ensure_windows_dev_ami(c: AWSConnect'
713 },
795 },
714 }
796 }
715 ],
797 ],
716 'ImageId': find_windows_server_2019_image(ec2resource).id,
798 'ImageId': image.id,
717 'InstanceInitiatedShutdownBehavior': 'stop',
799 'InstanceInitiatedShutdownBehavior': 'stop',
718 'InstanceType': 't3.medium',
800 'InstanceType': 't3.medium',
719 'KeyName': '%sautomation' % prefix,
801 'KeyName': '%sautomation' % prefix,
@@ -748,38 +830,14 b' def ensure_windows_dev_ami(c: AWSConnect'
748
830
749 # Compute a deterministic fingerprint to determine whether image needs
831 # Compute a deterministic fingerprint to determine whether image needs
750 # to be regenerated.
832 # to be regenerated.
751 fingerprint = {
833 fingerprint = resolve_fingerprint({
752 'instance_config': config,
834 'instance_config': config,
753 'user_data': WINDOWS_USER_DATA,
835 'user_data': WINDOWS_USER_DATA,
754 'initial_bootstrap': WINDOWS_BOOTSTRAP_POWERSHELL,
836 'initial_bootstrap': WINDOWS_BOOTSTRAP_POWERSHELL,
755 'bootstrap_commands': commands,
837 'bootstrap_commands': commands,
756 }
838 })
757
758 fingerprint = json.dumps(fingerprint, sort_keys=True)
759 fingerprint = hashlib.sha256(fingerprint.encode('utf-8')).hexdigest()
760
761 # Find existing AMIs with this name and delete the ones that are invalid.
762 # Store a reference to a good image so it can be returned one the
763 # image state is reconciled.
764 images = ec2resource.images.filter(
765 Filters=[{'Name': 'name', 'Values': [name]}])
766
767 existing_image = None
768
839
769 for image in images:
840 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
770 if image.tags is None:
771 print('image %s for %s lacks required tags; removing' % (
772 image.id, image.name))
773 remove_ami(ec2resource, image)
774 else:
775 tags = {t['Key']: t['Value'] for t in image.tags}
776
777 if tags.get('HGIMAGEFINGERPRINT') == fingerprint:
778 existing_image = image
779 else:
780 print('image %s for %s has wrong fingerprint; removing' % (
781 image.id, image.name))
782 remove_ami(ec2resource, image)
783
841
784 if existing_image:
842 if existing_image:
785 return existing_image
843 return existing_image
@@ -839,36 +897,9 b' def ensure_windows_dev_ami(c: AWSConnect'
839 run_powershell(instance.winrm_client, '\n'.join(commands))
897 run_powershell(instance.winrm_client, '\n'.join(commands))
840
898
841 print('bootstrap completed; stopping %s to create image' % instance.id)
899 print('bootstrap completed; stopping %s to create image' % instance.id)
842 instance.stop()
900 return create_ami_from_instance(ec2client, instance, name,
843
901 'Mercurial Windows development environment',
844 ec2client.get_waiter('instance_stopped').wait(
902 fingerprint)
845 InstanceIds=[instance.id],
846 WaiterConfig={
847 'Delay': 5,
848 })
849 print('%s is stopped' % instance.id)
850
851 image = instance.create_image(
852 Name=name,
853 Description='Mercurial Windows development environment',
854 )
855
856 image.create_tags(Tags=[
857 {
858 'Key': 'HGIMAGEFINGERPRINT',
859 'Value': fingerprint,
860 },
861 ])
862
863 print('waiting for image %s' % image.id)
864
865 ec2client.get_waiter('image_available').wait(
866 ImageIds=[image.id],
867 )
868
869 print('image %s available as %s' % (image.id, image.name))
870
871 return image
872
903
873
904
874 @contextlib.contextmanager
905 @contextlib.contextmanager
General Comments 0
You need to be logged in to leave comments. Login now