From f5be49a93fbdabe730fbf6a124dbbcce862f0388 Mon Sep 17 00:00:00 2001
From: Bo Maryniuk <bo@suse.de>
Date: Mon, 9 May 2016 10:33:44 +0200
Subject: [PATCH 07/38] Add SUSE Manager plugin

* Add unit test to the libzypp drift detector plugin
---
 scripts/zypper/plugins/commit/README.md   |  3 ++
 scripts/zypper/plugins/commit/susemanager | 59 ++++++++++++++++++++++++++++
 tests/unit/zypp_plugins_test.py           | 51 ++++++++++++++++++++++++
 tests/zypp_plugin.py                      | 64 +++++++++++++++++++++++++++++++
 4 files changed, 177 insertions(+)
 create mode 100644 scripts/zypper/plugins/commit/README.md
 create mode 100755 scripts/zypper/plugins/commit/susemanager
 create mode 100644 tests/unit/zypp_plugins_test.py
 create mode 100644 tests/zypp_plugin.py

diff --git a/scripts/zypper/plugins/commit/README.md b/scripts/zypper/plugins/commit/README.md
new file mode 100644
index 0000000..01c8917
--- /dev/null
+++ b/scripts/zypper/plugins/commit/README.md
@@ -0,0 +1,3 @@
+# Zypper plugins
+
+Plugins here are required to interact with SUSE Manager in conjunction of SaltStack and Zypper.
diff --git a/scripts/zypper/plugins/commit/susemanager b/scripts/zypper/plugins/commit/susemanager
new file mode 100755
index 0000000..268298b
--- /dev/null
+++ b/scripts/zypper/plugins/commit/susemanager
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2016 SUSE Linux LLC
+# All Rights Reserved.
+#
+# Author: Bo Maryniuk <bo@suse.de>
+
+import sys
+import os
+import hashlib
+
+from zypp_plugin import Plugin
+
+
+class DriftDetector(Plugin):
+    """
+    Return diff of the installed packages outside the Salt.
+    """
+    def __init__(self):
+        Plugin.__init__(self)
+        self.ck_path = "/var/cache/salt/minion/rpmdb.cookie"
+        self.rpm_path = "/var/lib/rpm/Packages"
+
+    def _get_mtime(self):
+        '''
+        Get the modified time of the RPM Database.
+        Returns:
+            Unix ticks
+        '''
+        return os.path.exists(self.rpm_path) and int(os.path.getmtime(self.rpm_path)) or 0
+
+    def _get_checksum(self):
+        '''
+        Get the checksum of the RPM Database.
+        Returns:
+            hexdigest
+        '''
+        digest = hashlib.md5()
+        with open(self.rpm_path, "rb") as rpm_db_fh:
+            while True:
+                buff = rpm_db_fh.read(0x1000)
+                if not buff:
+                    break
+                digest.update(buff)
+
+        return digest.hexdigest()
+
+    def PLUGINEND(self, headers, body):
+        """
+        Hook when plugin closes Zypper's transaction.        
+        """
+        if 'SALT_RUNNING' not in os.environ:
+            with open(self.ck_path, 'w') as ck_fh:
+                ck_fh.write('{chksum} {mtime}\n'.format(chksum=self._get_checksum(), mtime=self._get_mtime()))
+
+        self.ack()
+
+
+DriftDetector().main()
diff --git a/tests/unit/zypp_plugins_test.py b/tests/unit/zypp_plugins_test.py
new file mode 100644
index 0000000..6075288
--- /dev/null
+++ b/tests/unit/zypp_plugins_test.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+'''
+    :codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
+'''
+
+# Import Python Libs
+from __future__ import absolute_import
+
+# Import Salt Testing Libs
+from salttesting.helpers import ensure_in_syspath
+from salttesting import TestCase, skipIf
+from salttesting.mock import (
+    MagicMock,
+    patch,
+    NO_MOCK,
+    NO_MOCK_REASON
+)
+
+ensure_in_syspath('../')
+
+import os
+import imp
+from zypp_plugin import BogusIO
+
+susemanager = imp.load_source('susemanager', os.path.sep.join(os.path.dirname(__file__).split(
+    os.path.sep)[:-2] + ['scripts', 'zypper', 'plugins', 'commit', 'susemanager']))
+
+@skipIf(NO_MOCK, NO_MOCK_REASON)
+class ZyppPluginsTestCase(TestCase):
+    '''
+    Test shipped libzypp plugins.
+    '''
+    def test_drift_detector(self):
+        '''
+        Test drift detector for a correct cookie file.
+        Returns:
+
+        '''
+        drift = susemanager.DriftDetector()
+        drift._get_mtime = MagicMock(return_value=123)
+        drift._get_checksum = MagicMock(return_value='deadbeef')
+        bogus_io = BogusIO()
+        with patch('susemanager.open', bogus_io):
+            drift.PLUGINEND(None, None)
+        self.assertEqual(str(bogus_io), 'deadbeef 123\n')
+        self.assertEqual(bogus_io.mode, 'w')
+        self.assertEqual(bogus_io.path, '/var/cache/salt/minion/rpmdb.cookie')
+
+if __name__ == '__main__':
+    from integration import run_tests
+    run_tests(ZyppPluginsTestCase, needs_daemon=False)
diff --git a/tests/zypp_plugin.py b/tests/zypp_plugin.py
new file mode 100644
index 0000000..218f703
--- /dev/null
+++ b/tests/zypp_plugin.py
@@ -0,0 +1,64 @@
+'''
+Related to zypp_plugins_test.py module.
+'''
+
+
+class Plugin(object):
+    '''
+    Bogus module for Zypp Plugins tests.
+    '''
+    def ack(self):
+        '''
+        Acknowledge that the plugin had finished the transaction
+        Returns:
+
+        '''
+
+    def main(self):
+        '''
+        Register plugin
+        Returns:
+
+        '''
+
+
+class BogusIO(object):
+    '''
+    Read/write logger.
+    '''
+
+    def __init__(self):
+        self.content = list()
+        self.closed = False
+
+    def __str__(self):
+        return '\n'.join(self.content)
+
+    def __call__(self, *args, **kwargs):
+        self.path, self.mode = args
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def __enter__(self):
+        return self
+
+    def write(self, data):
+        '''
+        Simulate writing data
+        Args:
+            data:
+
+        Returns:
+
+        '''
+        self.content.append(data)
+
+    def close(self):
+        '''
+        Simulate closing the IO object.
+        Returns:
+
+        '''
+        self.closed = True
-- 
2.10.2

