You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/11/18 17:06:03 UTC
[04/16] allura git commit: [#7999] ticket:855 Add script task to
delete projects
[#7999] ticket:855 Add script task to delete projects
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/11449f20
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/11449f20
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/11449f20
Branch: refs/heads/ib/7999a
Commit: 11449f20952438c245059c91a8abfe225700c2a4
Parents: d457e58
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Nov 2 13:50:38 2015 +0200
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Nov 16 14:18:35 2015 +0200
----------------------------------------------------------------------
Allura/allura/scripts/delete_projects.py | 85 ++++++++++++++++++++++++
Allura/allura/tasks/index_tasks.py | 5 ++
Allura/allura/tests/test_delete_projects.py | 81 ++++++++++++++++++++++
3 files changed, 171 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/11449f20/Allura/allura/scripts/delete_projects.py
----------------------------------------------------------------------
diff --git a/Allura/allura/scripts/delete_projects.py b/Allura/allura/scripts/delete_projects.py
new file mode 100644
index 0000000..bef2d80
--- /dev/null
+++ b/Allura/allura/scripts/delete_projects.py
@@ -0,0 +1,85 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import argparse
+import logging
+
+from ming.odm import Mapper, session
+from pylons import app_globals as g
+
+from allura.scripts import ScriptTask
+from allura import model as M
+from allura.tasks.index_tasks import solr_del_project_artifacts
+
+
+log = logging.getLogger(__name__)
+
+
+class DeleteProjects(ScriptTask):
+
+ @classmethod
+ def execute(cls, options):
+ for proj in options.projects:
+ proj = cls.get_project(proj)
+ if proj:
+ log.info('Purging %s%s', proj.neighborhood.url_prefix, proj.shortname)
+ cls.purge_project(proj)
+
+ @classmethod
+ def get_project(cls, proj):
+ n, p = proj.split('/')
+ n = M.Neighborhood.query.get(url_prefix='/{}/'.format(n))
+ if not n:
+ log.warn("Can't find neighborhood for %s", proj)
+ return
+ p = M.Project.query.get(neighborhood_id=n._id, shortname=p)
+ if not p:
+ log.warn("Can't find project %s", proj)
+ return
+ return p
+
+ @classmethod
+ def purge_project(cls, project):
+ pid = project._id
+ solr_del_project_artifacts.post(pid)
+ app_config_ids = [ac._id for ac in M.AppConfig.query.find(dict(project_id=pid))]
+ for m in Mapper.all_mappers():
+ cls = m.mapped_class
+ if 'project_id' in m.property_index:
+ # Purge the things directly related to the project
+ cls.query.remove(dict(project_id=pid))
+ elif 'app_config_id' in m.property_index:
+ # ... and the things related to its apps
+ cls.query.remove(dict(app_config_id={'$in': app_config_ids}))
+ project.delete()
+ session(project).flush()
+ g.post_event('project_deleted', project_id=pid)
+
+ @classmethod
+ def parser(cls):
+ parser = argparse.ArgumentParser(description='Completely delete projects')
+ parser.add_argument('projects', metavar='nbhd/project', type=str, nargs='+',
+ help='Project to delete in a form nbhd_prefix/shortname')
+ return parser
+
+
+def get_parser():
+ return DeleteProjects.parser()
+
+
+if __name__ == '__main__':
+ DeleteProjects.main()
http://git-wip-us.apache.org/repos/asf/allura/blob/11449f20/Allura/allura/tasks/index_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tasks/index_tasks.py b/Allura/allura/tasks/index_tasks.py
index c9e853d..f5715b8 100644
--- a/Allura/allura/tasks/index_tasks.py
+++ b/Allura/allura/tasks/index_tasks.py
@@ -121,6 +121,11 @@ def del_artifacts(ref_ids):
@task
+def solr_del_project_artifacts(project_id):
+ g.solr.delete(q='project_id_s:%s' % project_id)
+
+
+@task
def commit():
g.solr.commit()
http://git-wip-us.apache.org/repos/asf/allura/blob/11449f20/Allura/allura/tests/test_delete_projects.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_delete_projects.py b/Allura/allura/tests/test_delete_projects.py
new file mode 100644
index 0000000..466fe80
--- /dev/null
+++ b/Allura/allura/tests/test_delete_projects.py
@@ -0,0 +1,81 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from ming.odm import session, Mapper
+from mock import patch
+
+from alluratest.controller import TestController
+
+from allura import model as M
+from allura.scripts import delete_projects
+
+
+class TestDeleteProjects(TestController):
+
+ def setUp(self):
+ super(TestDeleteProjects, self).setUp()
+ n = M.Neighborhood.query.get(name='Projects')
+ admin = M.User.by_username('test-admin')
+ self.p_shortname = 'test-delete'
+ n.register_project(self.p_shortname, admin)
+
+ def run_script(self, options):
+ cls = delete_projects.DeleteProjects
+ opts = cls.parser().parse_args(options)
+ cls.execute(opts)
+
+ def things_related_to_project(self, pid):
+ result = []
+ ac_ids = [ac._id for ac in M.AppConfig.query.find(dict(project_id=pid))]
+ for m in Mapper.all_mappers():
+ cls = m.mapped_class
+ things = None
+ if 'project_id' in m.property_index:
+ things = cls.query.find(dict(project_id=pid)).all()
+ elif 'app_config_id' in m.property_index:
+ things = cls.query.find(dict(app_config_id={'$in': ac_ids})).all()
+ if things:
+ result.extend(things)
+ return result
+
+ def test_project_is_deleted(self):
+ p = M.Project.query.get(shortname=self.p_shortname)
+ assert p is not None, 'Can not find project to delete'
+ self.run_script(['p/{}'.format(p.shortname)])
+ session(p).expunge(p)
+ p = M.Project.query.get(shortname=p.shortname)
+ assert p is None, 'Project is not deleted'
+
+ def test_artifacts_are_deleted(self):
+ pid = M.Project.query.get(shortname=self.p_shortname)._id
+ things = self.things_related_to_project(pid)
+ assert len(things) > 0, 'No things related to project to begin with'
+ self.run_script(['p/{}'.format(self.p_shortname)])
+ things = self.things_related_to_project(pid)
+ assert len(things) == 0, 'Not all things are deleted: %s' % things
+
+ @patch('allura.scripts.delete_projects.solr_del_project_artifacts', autospec=True)
+ def test_solr_index_is_deleted(self, del_solr):
+ pid = M.Project.query.get(shortname=self.p_shortname)._id
+ self.run_script(['p/{}'.format(self.p_shortname)])
+ del_solr.post.assert_called_once_with(pid)
+
+ @patch.object(delete_projects.g, 'post_event', autospec=True)
+ def test_event_is_fired(self, post_event):
+ pid = M.Project.query.get(shortname=self.p_shortname)._id
+ self.run_script(['p/{}'.format(self.p_shortname)])
+ post_event.assert_called_once_with('project_deleted', project_id=pid)