def stage_app(self, data): with self.database.session() as session: stage = session.query(Stage).join(Job, Stage.job_id == Job.id).filter(Stage.app == data.name). \ filter(Job.stopped_at == None).first() if stage is not None: raise cherrypy.HTTPError(400, "Staging for app " + data.name + " is already running") job = Job(name='App Stage ' + data.name, datacenter=self.config.datacenter, user_assignment_id=cherrypy.request.user['id']) session.add(job) session.flush() stage = Stage(app=data.name, job=job, type=data.type, branch=data.branch) session.add(stage) session.flush() if not os.path.isfile(self.config.app_type_path + "/" + stage.type + "-stage.json"): raise cherrypy.HTTPError(404, "Unknown app stage type " + stage.type) try: with open(self.config.app_type_path + "/" + stage.type + "-stage.json") as f: app_json = json.load(f) except IOError as e: raise cherrypy.HTTPError(500, "Error opening app type file " + e.message) except ValueError as e: raise cherrypy.HTTPError(500, "Error loading app type json " + e.message) try: app_type = AppTypeValidator(app_json) except ModelConversionError as e: raise cherrypy.HTTPError(400, "App Type JSON Error " + json.dumps(e.message)) try: app_type.validate() except ModelValidationError as e: raise cherrypy.HTTPError(400, "App Type JSON Error " + json.dumps(e.message)) if data.stage_tasks is not None: # Allow tasks to be overwritten for task_index, app_task in enumerate(list(app_type.tasks)): for config_task in list(data.stage_tasks): if app_task.name == config_task.name \ and app_task.priority == config_task.priority: app_type.tasks[task_index] = config_task data.stage_tasks.remove(config_task) app_type.tasks.extend(data.stage_tasks) app_type.tasks.sort(key=lambda x: x.priority) try: workers = self.get_workers(job.datacenter) except GetWorkersException as e: raise cherrypy.HTTPError(500, e.message) for worker in workers: if data.tags is not None: for key, value in data.tags.iteritems(): if key not in worker.tags: workers.remove(worker) continue if worker.tags[key] != value: workers.remove(worker) continue if len(workers) == 0: raise cherrypy.HTTPError(500, "No workers to build a stage on") worker = random.choice(workers) job_target = JobTarget(job=job, worker_id=worker.id) session.add(job_target) reobj = re.compile("{(.*?)}") variables = { 'target': worker.target, 'repo': data.repo, 'branch': data.branch, 'build_path': self.config.build_path, 'name': stage.app, 'app_name': stage.app, 'start_date': str(self.unix_time_millis(stage.job.created_at)), 'stage_id': str(stage.id) } if data.tags is not None: stage.job.tags = data.tags variables = variables.copy() variables.update(data.tags) if app_type.variables is not None: for key, value in dict(app_type.variables).iteritems(): variables[key] = value for key, value in app_type.variables.iteritems(): result = reobj.findall(value) for var_key in result: if var_key in variables: value = value.replace("{" + var_key + "}", variables[var_key]) variables[key] = value if data.stage_variables is not None: for key, value in dict(data.stage_variables).iteritems(): variables[key] = value for key, value in data.stage_variables.iteritems(): result = reobj.findall(value) for var_key in result: if var_key in variables: value = value.replace("{" + var_key + "}", variables[var_key]) variables[key] = value for task_index, task_data in enumerate(app_type.tasks): try: task_data.validate() except ModelValidationError as e: raise cherrypy.HTTPError(400, "Task validation error " + e.message) task = Task(name=task_data.name, order=task_index, job_target=job_target) session.add(task) job_target.tasks.append(task) for action_index, action in enumerate(task_data.actions): action_obj = Action(processor=action.processor, order=action_index) session.add(action_obj) task.actions.append(action_obj) if action.arguments is not None: for key in action.arguments: argument = action.arguments[key] result = reobj.findall(argument) for var_key in result: if var_key in variables: argument = argument.replace("{" + var_key + "}", variables[var_key]) action.arguments[key] = argument action_obj.arguments = action.arguments if len(job_target.tasks) == 0: raise cherrypy.HTTPError(400, "Stage has no tasks") session.commit() session.refresh(job) session.refresh(stage) return stage, job
def rollback_app(self, data): if data.datacenter is None: data.datacenter = self.config.datacenter with self.database.session() as session: rollback = ( session.query(Rollback) .join(Job, Rollback.job_id == Job.id) .filter(Rollback.app == data.app) .filter(Job.datacenter == data.datacenter) .filter(Rollback.environment == data.env) .filter(Job.stopped_at == None) .first() ) if rollback is not None: raise cherrypy.HTTPError( 400, "A rollback in " + data.datacenter + " of app " + rollback.app + " is already running in environment " + rollback.environment, ) deploy = ( session.query(Deploy) .join(Job, Deploy.job_id == Job.id) .join(Stage, Deploy.stage_id == Stage.id) .filter(Stage.app == data.app) .filter(Job.datacenter == data.datacenter) .filter(Deploy.environment == data.env) .filter(Job.stopped_at == None) .first() ) if deploy is not None: raise cherrypy.HTTPError( 400, "A deploy in " + data.datacenter + " of app " + deploy.stage.app + " is currently running in environment " + deploy.environment, ) job = Job( name="App Rollback " + data.app, datacenter=data.datacenter, user_assignment_id=cherrypy.request.user["id"], ) session.add(job) session.flush() rollback = Rollback(app=data.app, type=data.type, job=job, environment=data.env) session.add(rollback) session.flush() if not os.path.isfile(self.config.app_type_path + "/" + rollback.type + "-rollback.json"): raise cherrypy.HTTPError(404, "Unknown app rollback type " + rollback.type) try: with open(self.config.app_type_path + "/" + rollback.type + "-rollback.json") as f: app_json = json.load(f) except IOError as e: raise cherrypy.HTTPError(500, "Error opening app type file " + e.message) except ValueError as e: raise cherrypy.HTTPError(500, "Error loading app type json " + e.message) try: app_type = AppTypeValidator(app_json) except ModelConversionError as e: raise cherrypy.HTTPError(400, "App Type JSON Error " + json.dumps(e.message)) try: app_type.validate() except ModelValidationError as e: raise cherrypy.HTTPError(400, "App Type JSON Error " + json.dumps(e.message)) if data.rollback_tasks is not None: # Allow tasks to be overwritten for task_index, app_task in enumerate(list(app_type.tasks)): for config_task in list(data.rollback_tasks): if app_task.name == config_task.name and app_task.priority == config_task.priority: app_type.tasks[task_index] = config_task data.rollback_tasks.remove(config_task) app_type.tasks.extend(data.rollback_tasks) app_type.tasks.sort(key=lambda x: x.priority) try: workers = self.get_workers(job.datacenter) except GetWorkersException as e: raise cherrypy.HTTPError(500, e.message) for worker in list(workers): if worker.tags["environment"] != data.env: workers.remove(worker) continue if data.tags is not None: for key, value in data.tags.iteritems(): if key not in worker.tags: workers.remove(worker) continue if worker.tags[key] != value: workers.remove(worker) continue else: if data.app not in worker.tags["apps"]: workers.remove(worker) continue if len(workers) == 0: raise cherrypy.HTTPError(500, "No workers to rollback to") reobj = re.compile("{(.*?)}") variables = { "worker_count": str(len(workers)), "workers": json.dumps([worker.target for worker in workers]), "deploy_path": self.config.deploy_path, "name": data.app, "app_name": data.app, "start_date": rollback.job.created_at.strftime("%s"), "rollback_id": str(rollback.id), "environment": rollback.environment, } if data.tags is not None: rollback.job.tags = data.tags variables = variables.copy() variables.update(data.tags) if app_type.variables is not None: for key, value in dict(app_type.variables).iteritems(): variables[key] = value for key, value in app_type.variables.iteritems(): result = reobj.findall(value) for var_key in result: if var_key in variables: value = value.replace("{" + var_key + "}", variables[var_key]) variables[key] = value if data.rollback_variables is not None: for key, value in dict(data.rollback_variables).iteritems(): variables[key] = value for key, value in data.rollback_variables.iteritems(): result = reobj.findall(value) for var_key in result: if var_key in variables: value = value.replace("{" + var_key + "}", variables[var_key]) variables[key] = value for worker in workers: job_target = JobTarget(job=job, worker_id=worker.id) session.add(job_target) for task_index, task_data in enumerate(app_type.tasks): try: task_data.validate() except ModelValidationError as e: raise cherrypy.HTTPError(400, "Task validation error " + json.dumps(e.message)) task = Task(name=task_data.name, order=task_index, job_target=job_target) session.add(task) job_target.tasks.append(task) for action_index, action in enumerate(task_data.actions): action_obj = Action(processor=action.processor, order=action_index) session.add(action_obj) task.actions.append(action_obj) if action.arguments is not None: for key in action.arguments: argument = action.arguments[key] result = reobj.findall(argument) for var_key in result: if var_key in variables: argument = argument.replace("{" + var_key + "}", variables[var_key]) action.arguments[key] = argument action_obj.arguments = action.arguments if len(job_target.tasks) == 0: raise cherrypy.HTTPError(400, "Rollback has no tasks") session.commit() session.refresh(job) session.refresh(rollback) return rollback, job