def update_stack(self, service_name, cloudformation_template, changeset_id, cf_stack): try: cs_response = self.cloudformation_client.create_change_set( StackName=cf_stack.stack_name, TemplateBody=cloudformation_template, Capabilities=["CAPABILITY_IAM"], ChangeSetName=changeset_id) cs_id = cs_response['Id'] changes = self.cloudformation_client.describe_change_set( StackName=cf_stack.stack_name, ChangeSetName=cs_id) # delay for 2 seconds while waiting for changeset to create time.sleep(2) print( colored("The following stack update changes to be applied:", 'cyan')) print( colored( "================================================================================", 'cyan')) print(colored(changes, 'yellow')) print( colored( "================================================================================", 'cyan')) perform_update = query_yes_no("Perform changes?") if perform_update: self.cloudformation_client.execute_change_set( ChangeSetName=changeset_id, StackName=cf_stack.stack_name) waiter = CloudformationWaiter(self.cloudformation) print( colored( 'Cloudformation stack update in progress. Please check the AWS console!', color='green')) print(colored('Waiting on stack update...', color='magenta')) waiter.waiter.wait(StackName=service_name) print(colored('Stack update finished!', color='green')) else: self.cloudformation_client.delete_change_set( StackName=cf_stack.stack_name, ChangeSetName=cs_id) except ClientError as e: raise NovaError(str(e)) except WaiterError as e: raise NovaError(str(e))
def kms_generate_data_key(self, kms_key, context): try: return self.kms_client.generate_data_key(KeyId=kms_key, EncryptionContext=context, NumberOfBytes=64) except: raise NovaError("Could not generate key using KMS key %s: %s" % (kms_key, str(sys.exc_info()[0])))
def find_image(self, looking_for_tag): matching_images = [ d for d in self.docker_client.images() if d['RepoTags'] is not None and looking_for_tag in d['RepoTags'] ] if not len(matching_images) == 1: raise NovaError( "Could not find a docker image with a tag for: '%s'" % looking_for_tag) else: return matching_images[0]
def kms_decrypt(self, cipher, context): try: return self.kms_client.decrypt(CiphertextBlob=cipher, EncryptionContext=context) except ClientError as e: if e.response["Error"]["Code"] == "InvalidCiphertextException": if context is None: msg = ( "Could not decrypt hmac key with KMS. The credential may " "require that an encryption context be provided to decrypt it." ) else: msg = ( "Could not decrypt hmac key with KMS. The encryption " "context provided may not match the one used when the " "credential was stored.") else: msg = "Decryption error %s" % e raise NovaError(msg) except Exception as e: raise NovaError("Decryption error %s" % e)
def __add_stack(self, cft, service, template_bucket, aws_manager, include_docker=True): other_params = self.extra_parameters.copy() if 'DNS' in other_params and other_params.get( 'HostedZoneName') is None: record = other_params.get('DNS') hostedzones = aws_manager.get_hosted_zone_names() hostedzone = next((z for z in hostedzones if (z in record) or (z[:len(z) - 1] in record)), None) if hostedzone is not None: other_params['HostedZoneName'] = hostedzone else: raise NovaError( "Unable to determine 'HostedZoneName' from DNS record '%s'" % record) other_params.update([('StackType', self.stack_type), ('Port', service.port), ('ApplicationName', service.name), ('TeamName', service.team_name), ('HealthcheckUrl', service.healthcheck_url), ('LogsList', self.serialize_logs_settings(service.logs))]) if include_docker: other_params.update([ ('DockerDeploymentOptions', self.pickle_encode(self.deployment_options)), ('DockerDeploymentVariables', self.pickle_encode(self.deployment_variables)), ('DockerDeploymentVolumes', self.pickle_encode(self.deployment_volumes)), ('DockerDeploymentArguments', self.pickle_encode(self.deployment_arguments)) ]) parameters = { 'TemplateURL': self.template_url_to_use(service, template_bucket), 'TimeoutInMinutes': 60, 'Parameters': other_params } cft.resources.add( Resource(self.name.title(), 'AWS::CloudFormation::Stack', parameters))
def create_and_upload_stack_template(self, s3_bucket, service, environment): bucket = self.create_bucket( s3_bucket, 'Common Nova service templates bucket does not exist, creating...') for s, template in environment.get_stack_templates_used( service, s3_bucket).items(): if 's3_key' in template: template_file = template['filename'] template_s3_key = template['s3_key'] else: template_version = template['version'] template_filename = '%s.json' % template['name'] user_home_dir = os.path.join(os.path.expanduser('~'), '.nova') if not os.path.exists(user_home_dir): raise NovaError( "Unable to find NOVA templates. Please create templates under '~/.nova'." ) repo = git.Repo(user_home_dir) # If repo is dirty or has un-pushed commits we want to fail here if repo.is_dirty() or bool( repo.git.rev_list("origin/master..master")): raise NovaError( "You have local modifications to '~/.nova'. These are shared templates! Please create a pull request!" ) template_file = os.path.join(user_home_dir, template_version, template_filename) template_s3_key = '%s/%s' % (template_version, template_filename) existing_file = None if bucket: print( colored( 'Found common Nova service templates bucket. Verifying contents...', color='cyan')) checksum = self.__md5(template_file) try: existing_file = self.s3_client.head_object( Bucket=s3_bucket, Key=template_s3_key, IfMatch=checksum) except ClientError: pass if not existing_file: print( colored( 'Common Nova service templates not found. Uploading...', color='cyan')) with open(template_file, 'rb') as template_data: self.s3_client.put_object(Bucket=s3_bucket, Key=template_s3_key, Body=template_data) print(colored('Contents uploaded!', color='green')) else: print(colored('Contents verified!', color='green')) print( colored('All required templates have been uploaded & verified!', color='green'))