From ba070c39f6c1e7fc093f2a2b25a37c341cc4b43c Mon Sep 17 00:00:00 2001
From: Giorgos Korfiatis <gkorf@grnet.gr>
Date: Mon, 7 Oct 2013 15:56:17 +0300
Subject: [PATCH] astakos: Remove custom commit_on_success decorator

Prior to 1.3, django wouldn't set the transaction dirty upon a DB
exception and thus wouldn't trigger a rollback, resulting in a dangling
aborted DB exception. See https://code.djangoproject.com/ticket/9964

Since this is now fixed, there is no need for our custom transaction
management.
---
 snf-astakos-app/astakos/api/projects.py       | 26 +++++-----
 snf-astakos-app/astakos/api/quotas.py         |  9 ++--
 .../management/commands/component-remove.py   |  4 +-
 .../im/management/commands/project-control.py |  4 +-
 .../astakos/im/management/commands/quota.py   |  4 +-
 .../commands/reconcile-resources-astakos.py   |  4 +-
 .../im/management/commands/resource-import.py |  4 +-
 .../im/management/commands/service-import.py  |  4 +-
 .../im/management/commands/user-add.py        |  4 +-
 .../im/management/commands/user-modify.py     |  4 +-
 snf-astakos-app/astakos/im/views/projects.py  | 27 +++++-----
 snf-astakos-app/astakos/test/stress.py        |  4 +-
 snf-astakos-app/astakos/test/views.py         | 10 ++--
 .../snf_django/lib/db/transaction.py          | 52 -------------------
 14 files changed, 53 insertions(+), 107 deletions(-)
 delete mode 100644 snf-django-lib/snf_django/lib/db/transaction.py

diff --git a/snf-astakos-app/astakos/api/projects.py b/snf-astakos-app/astakos/api/projects.py
index 4bb775b12..fdfb98341 100644
--- a/snf-astakos-app/astakos/api/projects.py
+++ b/snf-astakos-app/astakos/api/projects.py
@@ -36,8 +36,8 @@ from django.utils import simplejson as json
 from django.views.decorators.csrf import csrf_exempt
 from django.http import HttpResponse
 from django.db.models import Q
+from django.db import transaction
 
-from snf_django.lib.db.transaction import commit_on_success_strict
 from astakos.api.util import json_response
 
 from snf_django.lib import api
@@ -271,7 +271,7 @@ def projects(request):
 
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def get_projects(request):
     user = request.user
     input_data = read_json_body(request, default={})
@@ -299,7 +299,7 @@ def _get_projects(query, request_user=None):
 
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def create_project(request):
     user = request.user
     data = request.body
@@ -319,7 +319,7 @@ def project(request, project_id):
 
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def get_project(request, project_id):
     user = request.user
     with ExceptionHandler():
@@ -337,7 +337,7 @@ def _get_project(project_id, request_user=None):
 
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def modify_project(request, project_id):
     user = request.user
     data = request.body
@@ -466,7 +466,7 @@ PROJECT_ACTION = {
 @csrf_exempt
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def project_action(request, project_id):
     user = request.user
     data = request.body
@@ -497,7 +497,7 @@ def make_application_query(input_data):
 
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def get_applications(request):
     user = request.user
     input_data = read_json_body(request, default={})
@@ -520,7 +520,7 @@ def _get_applications(query, request_user=None):
 @csrf_exempt
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def application(request, app_id):
     user = request.user
     with ExceptionHandler():
@@ -547,7 +547,7 @@ APPLICATION_ACTION = {
 @csrf_exempt
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def application_action(request, app_id):
     user = request.user
     data = request.body
@@ -581,7 +581,7 @@ def make_membership_query(input_data):
 
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def get_memberships(request):
     user = request.user
     input_data = read_json_body(request, default={})
@@ -631,7 +631,7 @@ MEMBERSHIPS_ACTION = {
 
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def post_memberships(request):
     user = request.user
     data = request.body
@@ -642,7 +642,7 @@ def post_memberships(request):
 
 @api.api_method(http_method="GET", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def membership(request, memb_id):
     user = request.user
     with ExceptionHandler():
@@ -669,7 +669,7 @@ MEMBERSHIP_ACTION = {
 @csrf_exempt
 @api.api_method(http_method="POST", token_required=True, user_required=False)
 @user_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def membership_action(request, memb_id):
     user = request.user
     input_data = read_json_body(request, default={})
diff --git a/snf-astakos-app/astakos/api/quotas.py b/snf-astakos-app/astakos/api/quotas.py
index 3bc4cd7d4..1475c8de0 100644
--- a/snf-astakos-app/astakos/api/quotas.py
+++ b/snf-astakos-app/astakos/api/quotas.py
@@ -34,8 +34,7 @@
 from django.utils import simplejson as json
 from django.views.decorators.csrf import csrf_exempt
 from django.http import HttpResponse
-
-from snf_django.lib.db.transaction import commit_on_success_strict
+from django.db import transaction
 
 from snf_django.lib import api
 from snf_django.lib.api.faults import BadRequest, ItemNotFound
@@ -175,7 +174,7 @@ def issue_commission(request):
     return json_response(data, status_code=status_code)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _issue_commission(clientkey, provisions, name, force, accept):
     serial = qh.issue_commission(clientkey=clientkey,
                                  provisions=provisions,
@@ -204,7 +203,7 @@ def conflictingCF(serial):
 @csrf_exempt
 @api.api_method(http_method='POST', token_required=True, user_required=False)
 @component_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def resolve_pending_commissions(request):
     data = request.body
     try:
@@ -260,7 +259,7 @@ def get_commission(request, serial):
 @csrf_exempt
 @api.api_method(http_method='POST', token_required=True, user_required=False)
 @component_from_token
-@commit_on_success_strict()
+@transaction.commit_on_success
 def serial_action(request, serial):
     data = request.body
     try:
diff --git a/snf-astakos-app/astakos/im/management/commands/component-remove.py b/snf-astakos-app/astakos/im/management/commands/component-remove.py
index e2593ecf1..b6db6626e 100644
--- a/snf-astakos-app/astakos/im/management/commands/component-remove.py
+++ b/snf-astakos-app/astakos/im/management/commands/component-remove.py
@@ -32,15 +32,15 @@
 # or implied, of GRNET S.A.
 
 from django.core.management.base import BaseCommand, CommandError
+from django.db import transaction
 from astakos.im.models import Component
-from snf_django.lib.db.transaction import commit_on_success_strict
 
 
 class Command(BaseCommand):
     args = "<component ID or name>"
     help = "Remove a component along with its registered services"
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
         if len(args) != 1:
             raise CommandError("Please provide a component ID or name")
diff --git a/snf-astakos-app/astakos/im/management/commands/project-control.py b/snf-astakos-app/astakos/im/management/commands/project-control.py
index 12981f819..4627e6e8a 100644
--- a/snf-astakos-app/astakos/im/management/commands/project-control.py
+++ b/snf-astakos-app/astakos/im/management/commands/project-control.py
@@ -33,11 +33,11 @@
 
 from optparse import make_option
 
+from django.db import transaction
 from django.core.management.base import BaseCommand, CommandError
 from astakos.im.functions import (terminate, suspend, unsuspend,
                                   reinstate, check_expiration,
                                   approve_application, deny_application)
-from snf_django.lib.db.transaction import commit_on_success_strict
 
 
 class Command(BaseCommand):
@@ -86,7 +86,7 @@ class Command(BaseCommand):
                           "e.g. when denying a project")),
     )
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
 
         message = options['message']
diff --git a/snf-astakos-app/astakos/im/management/commands/quota.py b/snf-astakos-app/astakos/im/management/commands/quota.py
index e848ab195..3cea21d66 100644
--- a/snf-astakos-app/astakos/im/management/commands/quota.py
+++ b/snf-astakos-app/astakos/im/management/commands/quota.py
@@ -33,13 +33,13 @@
 
 from optparse import make_option
 from django.core.management.base import CommandError
+from django.db import transaction
 
 from astakos.im.models import AstakosUser
 from astakos.im.quotas import (
     qh_sync_users_diffs, list_user_quotas, add_base_quota)
 from astakos.im.functions import get_user_by_uuid
 from astakos.im.management.commands._common import is_uuid, is_email
-from snf_django.lib.db.transaction import commit_on_success_strict
 from snf_django.management.commands import SynnefoCommand
 from snf_django.management import utils
 from ._common import show_quotas, style_options, check_style, units
@@ -87,7 +87,7 @@ class Command(SynnefoCommand):
                     ),
     )
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
         sync = options['sync']
         verify = options['verify']
diff --git a/snf-astakos-app/astakos/im/management/commands/reconcile-resources-astakos.py b/snf-astakos-app/astakos/im/management/commands/reconcile-resources-astakos.py
index 479589702..79d1849f1 100644
--- a/snf-astakos-app/astakos/im/management/commands/reconcile-resources-astakos.py
+++ b/snf-astakos-app/astakos/im/management/commands/reconcile-resources-astakos.py
@@ -34,9 +34,9 @@
 from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
+from django.db import transaction
 
 from snf_django.management.utils import pprint_table
-from snf_django.lib.db.transaction import commit_on_success_strict
 from astakos.im.models import Component, AstakosUser
 from astakos.im.quotas import service_get_quotas, SYSTEM
 from astakos.im.functions import count_pending_app
@@ -67,7 +67,7 @@ class Command(BaseCommand):
                          " the quota, independently of their value.")
     )
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
         write = self.stdout.write
         force = options['force']
diff --git a/snf-astakos-app/astakos/im/management/commands/resource-import.py b/snf-astakos-app/astakos/im/management/commands/resource-import.py
index a1917c0d0..c9dd997ca 100644
--- a/snf-astakos-app/astakos/im/management/commands/resource-import.py
+++ b/snf-astakos-app/astakos/im/management/commands/resource-import.py
@@ -33,10 +33,10 @@
 
 from optparse import make_option
 
+from django.db import transaction
 from django.core.management.base import BaseCommand, CommandError
 from django.utils import simplejson as json
 
-from snf_django.lib.db.transaction import commit_on_success_strict
 from astakos.im.register import add_resource, RegisterException
 from ._common import read_from_file
 
@@ -69,7 +69,7 @@ class Command(BaseCommand):
                 raise CommandError(m)
         self.add_resources(data)
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def add_resources(self, resources):
         output = []
         for resource in resources:
diff --git a/snf-astakos-app/astakos/im/management/commands/service-import.py b/snf-astakos-app/astakos/im/management/commands/service-import.py
index 2dd2e1d8f..087b9c67e 100644
--- a/snf-astakos-app/astakos/im/management/commands/service-import.py
+++ b/snf-astakos-app/astakos/im/management/commands/service-import.py
@@ -33,10 +33,10 @@
 
 from optparse import make_option
 
+from django.db import transaction
 from django.core.management.base import BaseCommand, CommandError
 from django.utils import simplejson as json
 
-from snf_django.lib.db.transaction import commit_on_success_strict
 from astakos.im.register import add_service, add_resource, RegisterException
 from astakos.im.models import Component
 from ._common import read_from_file
@@ -52,7 +52,7 @@ class Command(BaseCommand):
                     help="Load service definitions from a json file"),
     )
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
 
         json_file = options['json']
diff --git a/snf-astakos-app/astakos/im/management/commands/user-add.py b/snf-astakos-app/astakos/im/management/commands/user-add.py
index 05cefb1df..9d0fca21d 100644
--- a/snf-astakos-app/astakos/im/management/commands/user-add.py
+++ b/snf-astakos-app/astakos/im/management/commands/user-add.py
@@ -33,10 +33,10 @@
 
 from optparse import make_option
 
+from django.db import transaction
 from django.core.management.base import BaseCommand, CommandError
 from django.core.validators import validate_email
 from django.core.exceptions import ValidationError
-from snf_django.lib.db import transaction
 
 from astakos.im.models import AstakosUser
 
@@ -67,7 +67,7 @@ class Command(BaseCommand):
                     help="Add user permission (may be used multiple times)")
     )
 
-    @transaction.commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
         if len(args) != 3:
             raise CommandError("Invalid number of arguments")
diff --git a/snf-astakos-app/astakos/im/management/commands/user-modify.py b/snf-astakos-app/astakos/im/management/commands/user-modify.py
index f1fd2ebd8..607684af6 100644
--- a/snf-astakos-app/astakos/im/management/commands/user-modify.py
+++ b/snf-astakos-app/astakos/im/management/commands/user-modify.py
@@ -36,6 +36,7 @@ import string
 from optparse import make_option
 
 from django.core import management
+from django.db import transaction
 from django.core.management.base import BaseCommand, CommandError
 from django.contrib.auth.models import Group
 from django.core.exceptions import ValidationError
@@ -47,7 +48,6 @@ from astakos.im import quotas
 from astakos.im import activation_backends
 from ._common import (remove_user_permission, add_user_permission, is_uuid,
                       show_resource_value)
-from snf_django.lib.db.transaction import commit_on_success_strict
 
 activation_backend = activation_backends.get_backend()
 
@@ -151,7 +151,7 @@ class Command(BaseCommand):
                     help="Delete user"),
     )
 
-    @commit_on_success_strict()
+    @transaction.commit_on_success
     def handle(self, *args, **options):
         if len(args) != 1:
             raise CommandError("Please provide a user ID")
diff --git a/snf-astakos-app/astakos/im/views/projects.py b/snf-astakos-app/astakos/im/views/projects.py
index d47f1dbe4..91f6ace28 100644
--- a/snf-astakos-app/astakos/im/views/projects.py
+++ b/snf-astakos-app/astakos/im/views/projects.py
@@ -48,8 +48,7 @@ from django.utils.translation import ugettext as _
 from django.views.generic.list_detail import object_list, object_detail
 from django.core.exceptions import PermissionDenied
 from django.views.decorators.http import require_http_methods
-
-from snf_django.lib.db.transaction import commit_on_success_strict
+from django.db import transaction
 
 import astakos.im.messages as astakos_messages
 
@@ -123,7 +122,7 @@ def project_add(request):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def create_app_object(request, extra_context=None):
     try:
         summary = 'im/projects/projectapplication_form_summary.html'
@@ -191,7 +190,7 @@ def project_app_cancel(request, application_id):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_app_cancel(request, application_id):
     chain_id = None
     try:
@@ -264,7 +263,7 @@ def project_modify(request, application_id):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def update_app_object(request, object_id, extra_context=None):
     try:
         summary = 'im/projects/projectapplication_form_summary.html'
@@ -296,7 +295,7 @@ def project_detail(request, chain_id):
     return common_detail(request, chain_id)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def addmembers(request, chain_id, addmembers_form):
     if addmembers_form.is_valid():
         try:
@@ -476,7 +475,7 @@ def project_join(request, chain_id):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_join(request, chain_id):
     try:
         chain_id = int(chain_id)
@@ -505,7 +504,7 @@ def project_leave(request, memb_id):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_leave(request, memb_id):
     try:
         memb_id = int(memb_id)
@@ -534,7 +533,7 @@ def project_cancel_member(request, memb_id):
     return redirect(next)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_cancel_member(request, memb_id):
     try:
         cancel_membership(memb_id, request.user)
@@ -555,7 +554,7 @@ def project_accept_member(request, memb_id):
     return redirect_back(request, 'project_list')
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_accept_member(request, memb_id):
     try:
         memb_id = int(memb_id)
@@ -580,7 +579,7 @@ def project_remove_member(request, memb_id):
     return redirect_back(request, 'project_list')
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_remove_member(request, memb_id):
     try:
         memb_id = int(memb_id)
@@ -604,7 +603,7 @@ def project_reject_member(request, memb_id):
     return redirect_back(request, 'project_list')
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_reject_member(request, memb_id):
     try:
         memb_id = int(memb_id)
@@ -641,7 +640,7 @@ def project_app_approve(request, application_id):
     return redirect(reverse('project_detail', args=(chain_id,)))
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_app_approve(request, application_id):
     approve_application(application_id)
 
@@ -671,7 +670,7 @@ def project_app_deny(request, application_id):
     return redirect(reverse('project_list'))
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def _project_app_deny(request, application_id, reason):
     deny_application(application_id, reason=reason)
 
diff --git a/snf-astakos-app/astakos/test/stress.py b/snf-astakos-app/astakos/test/stress.py
index 4f62677d3..3aab7113c 100755
--- a/snf-astakos-app/astakos/test/stress.py
+++ b/snf-astakos-app/astakos/test/stress.py
@@ -46,11 +46,11 @@ path = os.path.dirname(os.path.realpath(__file__))
 os.environ['SYNNEFO_SETTINGS_DIR'] = path + '/settings'
 os.environ['DJANGO_SETTINGS_MODULE'] = 'synnefo.settings'
 
+from django.db import transaction
 from astakos.im.models import AstakosUser
 from astakos.im.functions import ProjectError
 from astakos.im import quotas
 from views import submit, approve, join, leave
-from snf_django.lib.db.transaction import commit_on_success_strict
 
 USERS = {}
 PROJECTS = {}
@@ -90,7 +90,7 @@ def new_user():
     return None
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def new_users(count):
     for i in range(count):
         while True:
diff --git a/snf-astakos-app/astakos/test/views.py b/snf-astakos-app/astakos/test/views.py
index 8e658b9b7..549394556 100644
--- a/snf-astakos-app/astakos/test/views.py
+++ b/snf-astakos-app/astakos/test/views.py
@@ -33,25 +33,25 @@
 
 from datetime import datetime, timedelta
 
+from django.db import transaction
 from astakos.im.models import AstakosUser, Project
 from astakos.im.functions import (join_project, leave_project,
                                   submit_application, approve_application,
                                   check_pending_app_quota,
                                   ProjectForbidden)
-from snf_django.lib.db.transaction import commit_on_success_strict
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def join(proj_id, user):
     return join_project(proj_id, user)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def leave(memb_id, request_user):
     return leave_project(memb_id, request_user)
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def submit(name, user_id, project_id=None):
     try:
         owner = AstakosUser.objects.get(id=user_id)
@@ -79,6 +79,6 @@ def submit(name, user_id, project_id=None):
     return app.id, app.chain_id
 
 
-@commit_on_success_strict()
+@transaction.commit_on_success
 def approve(app_id):
     approve_application(app_id)
diff --git a/snf-django-lib/snf_django/lib/db/transaction.py b/snf-django-lib/snf_django/lib/db/transaction.py
deleted file mode 100644
index eb6d33013..000000000
--- a/snf-django-lib/snf_django/lib/db/transaction.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2013 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-#   1. Redistributions of source code must retain the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer.
-#
-#   2. Redistributions in binary form must reproduce the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer in the documentation and/or other materials
-#      provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-
-from functools import wraps
-from django.db import transaction
-
-
-def commit_on_success_strict(**kwargs):
-    def wrap(func):
-        @wraps(func)
-        @transaction.commit_manually(**kwargs)
-        def inner(*args, **kwargs):
-            try:
-                result = func(*args, **kwargs)
-                transaction.commit()
-                return result
-            except BaseException as e:
-                transaction.rollback()
-                raise
-        return inner
-    return wrap
-- 
GitLab