Example #1
0
    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