def __init__(self, environment_name, nova_descriptor_file=None): self._nova_descriptor_file = nova_descriptor_file or 'nova.yml' self._environment_name = environment_name self._environment = None self._codedeploy_app = None self.templates_used = dict() yaml.add_constructor("!include", yaml_include) with open(os.path.join(spec.__path__[0], 'nova_service_schema.yml'), 'r') as schemaYaml: schema = yaml.load(schemaYaml) v = Validator(schema) try: with open(self._nova_descriptor_file, 'r') as novaYaml: self.service_spec = yaml.safe_load(novaYaml) # Validate loaded dictionary valid = v.validate(self.service_spec) if not valid: raise NovaError( "Invalid nova service descriptor file '%s': %s" % (self._nova_descriptor_file, v.errors)) else: self.service = Service.load(self.service_spec) self.service_name = self.service.name self.service_port = self.service.port self.service_healthcheck_url = self.service.healthcheck_url except IOError: raise NovaError("No nova service descriptor found at '%s'" % self._nova_descriptor_file)
def __init__(self, stash_key, manager_provider, aws_profile=None, aws_region=None, aws_bucket=None): check_latest_version() self._aws_manager = manager_provider.aws_manager(aws_profile, aws_region or 'us-east-1') if aws_bucket is None: deployment_bucket_name = 'novastash_%s' % self._aws_manager.account_alias else: deployment_bucket_name = aws_bucket key = "%s.txt.enc" % stash_key existing_stash = self._aws_manager.s3_get(deployment_bucket_name, key) if existing_stash is None: raise NovaError("No stash '%s' found!" % stash_key) else: contents = existing_stash['Body'].read() metadata = existing_stash['Metadata'] encryption_key = metadata['encryption-key'] kms_response = self._aws_manager.kms_decrypt(b64decode(encryption_key), {}) key = kms_response['Plaintext'][:32] hmac_key = kms_response['Plaintext'][32:] hmac = HMAC(hmac_key, msg=b64decode(contents), digestmod=SHA256) if hmac.hexdigest() != metadata['hmac']: raise NovaError("Computed HMAC on '%s' does not match stored HMAC" % stash_key) dec_ctr = Counter.new(128) decryptor = AES.new(key, AES.MODE_CTR, counter=dec_ctr) print(decryptor.decrypt(b64decode(contents)).decode("utf-8"))
def default(self): profile = self.app.pargs.profile deploy_args = self.app.pargs.deploy_args nova_descriptor_file = self.app.pargs.file manager_provider = ManagerProvider() deploy = not self.app.pargs.no_deploy if len(deploy_args) == 2: deployer = DeployStack(deploy_args[0], deploy_args[1], profile, manager_provider, deploy=deploy, nova_descriptor_file=nova_descriptor_file) elif len(deploy_args) == 3: deployer = DeployStack(deploy_args[0], deploy_args[1], profile, manager_provider, version=deploy_args[2], deploy=deploy, nova_descriptor_file=nova_descriptor_file) else: raise NovaError(INCORRECT_ARGS_USAGE) deployer.deploy()
def __init__(self, environment_name, stack_name, aws_profile, manager_provider, version=None, deploy=True, nova_descriptor_file=None): self._environment_name = environment_name self._stack_name = stack_name self._deploy = deploy self._version_manager = manager_provider.version_manager() self._docker_manager = manager_provider.docker_manager() self._service_manager = NovaServiceLoader(environment_name, nova_descriptor_file) self._aws_manager = manager_provider.aws_manager( aws_profile or self._service_manager.environment.aws_profile, self._service_manager.environment.aws_region) self._build_id = self._version_manager.current_version(version) self._stack = self._service_manager.get_stack(self._stack_name) self._code_deploy_app = self._service_manager.code_deploy_app if self._stack.deployment_group is None: raise NovaError( "Environment '%s' stack '%s' does not have 'deployment_group' set!" % (self._environment_name, self._stack.name)) self._nova_deploy_dir = None self._custom_scripts = CustomScripts()
def __cleanup(self): try: if os.path.exists(self._nova_deploy_dir): print( colored("Cleaning up deployment bundle...", color='green')) shutil.rmtree(self._nova_deploy_dir) except Exception as e: raise NovaError(e)
def get_stack(self, stack_name): stack = next((item for item in self.stacks if item.name == stack_name), None) if stack is not None: return stack else: raise NovaError("Stack '%s' was not found in the configuration" % stack_name)
def create(self): try: self._aws_manager.create_and_upload_stack_template(self._s3_bucket, self._service_manager.service, self._service_manager.environment) self._aws_manager.create_stack( self._service_manager.service_name, self.cloudformation_template ) stack = self._aws_manager.get_stack(self._service_manager.service_name) if stack.stack_status == "CREATE_COMPLETE": if stack.outputs is not None: for output in stack.outputs: if output.get('OutputKey') == 'CodeDeployApp': code_deploy_app_id = output.get('OutputValue') print(colored("Found code-deploy app: %s" % code_deploy_app_id, color='green')) self._service_manager.service.set_code_deploy_app(self._environment_name, code_deploy_app_id) print(colored("Please update nova.yml environment with 'deployment_application_id' manually.", color='yellow')) break for stack in self._service_manager.environment.stacks: if output.get('OutputKey') == stack.name.title() + 'DeploymentGroup': code_deploy_app_id = output.get('OutputValue') print(colored("Found code-deploy deployment group for stack '%s': %s" % (stack.name, code_deploy_app_id), color='green')) self._service_manager.service.set_code_deploy_app(self._environment_name, code_deploy_app_id) print(colored("Please update nova.yml environment with 'deployment_application_id' manually.", color='yellow')) else: print(colored("Unable to find code-deploy deployment group for stack '%s' in the stack output" % stack.name, 'yellow')) print(colored("Please update nova.yml stack with the 'deployment_group' manually.", color='yellow')) else: print(colored('Unable to find code-deploy application in the stack output', 'yellow')) print(colored("Please update nova.yml environment with 'deployment_application_id'", color='yellow')) print(colored("and your stacks with their respective 'deployment_group' manually.", color='yellow')) else: print(colored("Please update nova.yml environment with 'deployment_application_id'", color='yellow')) print(colored("and your stacks with their respective 'deployment_group' manually.", color='yellow')) else: raise NovaError("Stack creation was un-successful: %s" % stack.stack_status_reason) except ClientError as e: raise NovaError(str(e)) except WaiterError as e: raise NovaError(str(e))
def code_deploy_app(self): if self.environment.deployment_application_id is None: raise NovaError( "Environment '%s' does not have 'deployment_application_id' set!" % self._environment_name) if self._codedeploy_app is None: self._codedeploy_app = self.environment.deployment_application_id return self._codedeploy_app
def get_environment(self, environment_name): environment = next( (item for item in self.environments if item.name == environment_name), None) if environment is not None: return environment else: raise NovaError( "Environment '%s' was not found in the configuration" % environment_name)
def update(self): cf_template_out = self.app.pargs.output include_docker = self.app.pargs.include_docker if self.app.pargs.environment: profile = self.app.pargs.profile UpdateStack(aws_profile=profile, environment_name=self.app.pargs.environment[0], manager_provider=ManagerProvider(), cf_template_out=cf_template_out, include_docker=include_docker).update() else: raise NovaError(INCORRECT_UPDATE_ARGS_USAGE)
def create(self): cf_template_out = self.app.pargs.output nova_descriptor_file = self.app.pargs.file include_docker = self.app.pargs.include_docker if self.app.pargs.environment: profile = self.app.pargs.profile CreateStack(aws_profile=profile, environment_name=self.app.pargs.environment[0], manager_provider=ManagerProvider(), cf_template_out=cf_template_out, nova_descriptor_file=nova_descriptor_file, include_docker=include_docker).create() else: raise NovaError(INCORRECT_CREATE_ARGS_USAGE)
def __read_extra_resources(self): if self.resources: try: with open(self.resources, 'r') as extra_resources_file: rjson = json.load(extra_resources_file) return [ JSONableDict(update_dict=r[1], name=r[0]) for r in iter(rjson.items()) ] except IOError: raise NovaError("Environment resources '%s' not found." % self.resources) else: return None
def get(self): if len(self.app.pargs.stash_args) == 1: profile = self.app.pargs.profile region = self.app.pargs.region bucket = self.app.pargs.bucket key = self.app.pargs.stash_args[0] manager_provider = ManagerProvider() Decrypt(stash_key=key, manager_provider=manager_provider, aws_profile=profile, aws_region=region, aws_bucket=bucket) else: raise NovaError(INCORRECT_GET_ARGS_USAGE)
def update(self): self._aws_manager.create_and_upload_stack_template(self._s3_bucket, self._service_manager.service, self._service_manager.environment) changeset_id = "cs%s" % VersionManager().current_version().replace(".", "x") cf_stack = self._aws_manager.get_stack(self._service_manager.service_name) performed_update = self._aws_manager.update_stack( self._service_manager.service_name, self.cloudformation_template, changeset_id, cf_stack ) if performed_update: stack = self._aws_manager.get_stack(self._service_manager.service_name) if stack.stack_status == "UPDATE_COMPLETE": if stack.outputs is not None: for output in stack.outputs: if output.get('OutputKey') == 'CodeDeployApp': code_deploy_app_id = output.get('OutputValue') print("Found code-deploy app: %s" % code_deploy_app_id) self._service_manager.service.set_code_deploy_app(self.environment_name, code_deploy_app_id) print(green("Please update nova.yml environment with 'deployment_application_id' manually.")) break for stack in self._service_manager.environment.stacks: if output.get('OutputKey') == stack.name.title() + 'DeploymentGroup': code_deploy_app_id = output.get('OutputValue') print(colored("Found code-deploy deployment group for stack '%s': %s" % ( stack.name, code_deploy_app_id), color='green')) self._service_manager.service.set_code_deploy_app(self.environment_name, code_deploy_app_id) print(green("Please update nova.yml environment with 'deployment_application_id' manually.")) else: print(colored( "Unable to find code-deploy deployment group for stack '%s' in the stack output" % stack.name, 'yellow')) print(colored("Please update nova.yml stack with the 'deployment_group' manually.", color='yellow')) else: print(colored('Unable to find code-deploy application in the stack output', 'yellow')) print(colored("Please update nova.yml environment with 'deployment_application_id' manually.", color='yellow')) else: print(colored("Please update nova.yml environment with 'deployment_application_id' manually.", color='yellow')) print(colored("and your stacks with their respective 'deployment_group' manually.", color='yellow')) else: raise NovaError("Stack update was un-successful: %s - %s" % (stack.stack_status, stack.stack_status_reason))
def put(self): if len(self.app.pargs.stash_args) == 2: profile = self.app.pargs.profile region = self.app.pargs.region bucket = self.app.pargs.bucket kms_key_alias = self.app.pargs.key or 'novastash' key = self.app.pargs.stash_args[0] value = self.app.pargs.stash_args[1] manager_provider = ManagerProvider() Encrypt(stash_key=key, value=value, manager_provider=manager_provider, aws_profile=profile, aws_region=region, aws_bucket=bucket, kms_key="alias/%s" % kms_key_alias) else: raise NovaError(INCORRECT_PUT_ARGS_USAGE)
def check_latest_version(): try: current = pkg_resources.require("gilt-nova")[0].version pypi = six.moves.xmlrpc_client.ServerProxy( 'http://pypi.python.org/pypi') available = pypi.package_releases('gilt-nova') major_available = available[0].split('.')[0] major_current = current.split('.')[0] if available[0] != current: print( colored("The latest version of nova is '%s', please upgrade!" % available[0], color='yellow')) if major_available != major_current: raise NovaError( 'There has been a breaking change, please upgrade before continuing!' ) except Exception: pass
def __init__(self, stash_key, value, manager_provider, aws_profile=None, aws_region=None, aws_bucket=None, kms_key='alias/novastash'): check_latest_version() self._aws_manager = manager_provider.aws_manager( aws_profile, aws_region or 'us-east-1') if aws_bucket is None: deployment_bucket_name = 'novastash_%s' % self._aws_manager.account_alias else: deployment_bucket_name = aws_bucket if not self._aws_manager.kms_key_exists(kms_key): raise NovaError("Please setup the novastash KMS key.") self._aws_manager.create_bucket( deployment_bucket_name, "Creating novastash bucket '%s'" % deployment_bucket_name) # generate a a 64 byte key. # Half will be for data encryption, the other half for HMAC kms_response = self._aws_manager.kms_generate_data_key(kms_key, {}) data_key = tobytes(kms_response['Plaintext'][:32]) hmac_key = tobytes(kms_response['Plaintext'][32:]) wrapped_key = tobytes(kms_response['CiphertextBlob']) enc_ctr = Counter.new(128) encryptor = AES.new(data_key, AES.MODE_CTR, counter=enc_ctr) c_text = encryptor.encrypt(tobytes(value)) # compute an HMAC using the hmac key and the ciphertext hmac = HMAC(hmac_key, msg=c_text, digestmod=SHA256) b64hmac = hmac.hexdigest() key = "%s.txt.enc" % stash_key existing_stash = self._aws_manager.s3_head(deployment_bucket_name, key) if existing_stash is None: print(colored("Stashing '%s'" % stash_key)) self._aws_manager.s3_put( deployment_bucket_name, b64encode(c_text).decode('utf-8'), key, { 'encryption-key': b64encode(wrapped_key).decode('utf-8'), 'hmac': b64hmac }) else: perform_overwrite = query_yes_no( "Stash '%s' already exists, want to overwrite?" % stash_key, default="no") if perform_overwrite: self._aws_manager.s3_put( deployment_bucket_name, b64encode(c_text).decode('utf-8'), key, { 'encryption-key': b64encode(wrapped_key).decode('utf-8'), 'hmac': b64hmac }) else: print(colored("Not stashing anything for key '%s'" % stash_key))