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)