From 7be0894fce064b03e121d2952a0787936f42ec0c Mon Sep 17 00:00:00 2001
From: Stavros Sachtouris <saxtouri@admin.grnet.gr>
Date: Thu, 28 May 2015 18:52:00 +0300
Subject: [PATCH] On SSL error, fall back to default certificates

Create a decorator "ssl_fall_back" which catches a KamakiSSLError,
patches kamaki with the default certificates bundle and reruns the
failed method.

The decorator is used on the "SyncerSettings._get_pithos_client"
method, because this is the first point of SSL failure.

The default certificates bundle is acquired from the package
"certifi", which has been added to dependencies.
---
 agkyra/syncer/setup.py | 24 +++++++++++++++++++++---
 setup.py               |  1 +
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/agkyra/syncer/setup.py b/agkyra/syncer/setup.py
index 5ac804e..34517be 100644
--- a/agkyra/syncer/setup.py
+++ b/agkyra/syncer/setup.py
@@ -22,7 +22,7 @@ from agkyra.syncer.database import SqliteFileStateDB
 from agkyra.syncer.messaging import Messager
 from agkyra.syncer import utils
 
-from kamaki.clients import ClientError
+from kamaki.clients import ClientError, KamakiSSLError
 
 from kamaki.clients.astakos import AstakosClient
 from kamaki.clients.pithos import PithosClient
@@ -50,6 +50,23 @@ def get_instance(elems):
     return utils.hash_string(data)
 
 
+def ssl_fall_back(method):
+    """Catch an SSL error while executing a method, patch kamaki and retry"""
+    def wrap(self, *args, **kwargs):
+        try:
+            return method(self, *args, **kwargs)
+        except KamakiSSLError as ssle:
+            logger.debug('Kamaki SSL failed %s' % ssle)
+            logger.info(
+                'Kamaki SSL failed, fall back to certifi (mozilla certs)')
+            import certifi
+            https.patch_with_certs(certifi.where())
+            return method(self, *args, **kwargs)
+    wrap.__name__ = method.__name__
+    wrap.__doc__ = method.__doc__
+    return wrap
+
+
 class SyncerSettings():
     def __init__(self, auth_url, auth_token, container, local_root_path,
                  *args, **kwargs):
@@ -155,6 +172,7 @@ class SyncerSettings():
         os.mkdir(path)
         return path
 
+    @ssl_fall_back
     def _get_pithos_client(self, auth_url, token, container):
         try:
             astakos = AstakosClient(auth_url, token)
@@ -176,8 +194,8 @@ class SyncerSettings():
             client.get_container_info(container)
         except ClientError as e:
             if e.status == 404:
-                logger.warning("Container '%s' does not exist, creating..."
-                               % container)
+                logger.warning(
+                    "Container '%s' does not exist, creating..." % container)
                 try:
                     client.create_container(container)
                 except ClientError:
diff --git a/setup.py b/setup.py
index 4d32e98..8cbfae1 100644
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,7 @@ INSTALL_REQUIRES = [
     'watchdog',
     'psutil',
     'ws4py',
+    'certifi',
 ]
 
 EXTRAS_REQUIRES = {
-- 
GitLab