From 66ae6bcfbdbceb8e8f3af621c7b528a2a9332b42be2ea303de3b639daef4de04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Thu, 16 Feb 2017 15:09:53 +0000 Subject: [PATCH 1/3] Accepting request 458508 from home:bmaryniuk:branches:systemsmanagement:saltstack Update to 2016.11.2 version, bugfixes OBS-URL: https://build.opensuse.org/request/show/458508 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=85 --- 0005-Use-SHA256-hash-type-by-default.patch | 53 - ...salt-proxy-instantiated-service-file.patch | 36 - 0008-snapper-execution-module.patch | 1278 ----------------- 0011-Fix-snapper_test-for-python26.patch | 113 -- 0012-Fix-pkg.upgrade-for-zypper.patch | 33 - ...ains-for-SLES-Expanded-Support-SUSE-.patch | 35 - ...-fix-position-of-X-option-to-setfacl.patch | 42 - ...ersion-when-latest-already-installed.patch | 25 - ...olver-params-for-Zypper-debug-solver.patch | 74 - add-a-salt-minion-service-control-file.patch | 35 + add-buildargs-option-to-dockerng.build.patch | 48 + add-ssh-option-to-salt-ssh.patch | 163 +++ add-yum-plugin.patch | 116 ++ ...ugin.patch => add-zypp-notify-plugin.patch | 15 +- ...n-sles-12-sp2-because-of-new-systemd.patch | 26 + ...nfiguration-file-to-use-salt-toaster.patch | 9 +- ...trings-are-properly-encoded-in-utf-8.patch | 19 +- ...a-date-in-a-comment-to-prevent-rebui.patch | 11 +- ...-require-minimal-dockerpy-1.6.0-and-.patch | 48 + fix-a-typo-for-master-comments.patch | 32 + fix-error-parsing.patch | 26 + fix-leading-dots-on-sanitized-hostname.patch | 94 ++ ...-to-count-not-responding-minions-cor.patch | 11 +- ...dling-for-rpm-installtime-bsc-101707.patch | 34 + ...eouts-for-running-integrations-tests.patch | 170 +++ ...crash-on-unknown-to-the-repo-package.patch | 26 + ...-exception-in-case-path-doesn-t-exis.patch | 28 + ...run-salt-api-as-user-salt-bsc-990029.patch | 9 +- ...n-salt-master-as-dedicated-salt-user.patch | 11 +- salt-2016.11.2.tar.gz | 3 + salt-2016.3.4.tar.gz | 3 - salt.changes | 60 + salt.spec | 163 ++- ...ains-for-sles-expanded-support-suse-.patch | 28 + snapper-module-improvements.patch | 275 ++++ support-dryrun-for-dockerng.sls_build.patch | 187 +++ ...-we-don-t-have-python-systemd-so-not.patch | 9 +- 37 files changed, 1555 insertions(+), 1793 deletions(-) delete mode 100644 0005-Use-SHA256-hash-type-by-default.patch delete mode 100644 0006-Create-salt-proxy-instantiated-service-file.patch delete mode 100644 0008-snapper-execution-module.patch delete mode 100644 0011-Fix-snapper_test-for-python26.patch delete mode 100644 0012-Fix-pkg.upgrade-for-zypper.patch delete mode 100644 0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch delete mode 100644 0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch delete mode 100644 0016-Fix-pkg.latest_version-when-latest-already-installed.patch delete mode 100644 0017-Including-resolver-params-for-Zypper-debug-solver.patch create mode 100644 add-a-salt-minion-service-control-file.patch create mode 100644 add-buildargs-option-to-dockerng.build.patch create mode 100644 add-ssh-option-to-salt-ssh.patch create mode 100644 add-yum-plugin.patch rename 0007-Add-zypp-notify-plugin.patch => add-zypp-notify-plugin.patch (96%) create mode 100644 avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch rename 0015-Change-travis-configuration-file-to-use-salt-toaster.patch => change-travis-configuration-file-to-use-salt-toaster.patch (93%) rename 0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch => check-if-byte-strings-are-properly-encoded-in-utf-8.patch (80%) rename 0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch => do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch (67%) create mode 100644 docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch create mode 100644 fix-a-typo-for-master-comments.patch create mode 100644 fix-error-parsing.patch create mode 100644 fix-leading-dots-on-sanitized-hostname.patch rename 0009-fix-salt-summary-to-count-not-responding-minions-cor.patch => fix-salt-summary-to-count-not-responding-minions-cor.patch (77%) create mode 100644 fix-timezone-handling-for-rpm-installtime-bsc-101707.patch create mode 100644 increasing-timeouts-for-running-integrations-tests.patch create mode 100644 prevent-crash-on-unknown-to-the-repo-package.patch create mode 100644 prevents-oserror-exception-in-case-path-doesn-t-exis.patch rename 0010-Run-salt-api-as-user-salt-bsc-990029.patch => run-salt-api-as-user-salt-bsc-990029.patch (74%) rename 0002-Run-salt-master-as-dedicated-salt-user.patch => run-salt-master-as-dedicated-salt-user.patch (84%) create mode 100644 salt-2016.11.2.tar.gz delete mode 100644 salt-2016.3.4.tar.gz create mode 100644 setting-up-os-grains-for-sles-expanded-support-suse-.patch create mode 100644 snapper-module-improvements.patch create mode 100644 support-dryrun-for-dockerng.sls_build.patch rename 0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch => tserong-suse.com-we-don-t-have-python-systemd-so-not.patch (73%) diff --git a/0005-Use-SHA256-hash-type-by-default.patch b/0005-Use-SHA256-hash-type-by-default.patch deleted file mode 100644 index 85f2dfb..0000000 --- a/0005-Use-SHA256-hash-type-by-default.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 7854a6cd0ddc5a7d51f298e69b54eec09ce97c14 Mon Sep 17 00:00:00 2001 -From: Bo Maryniuk -Date: Thu, 17 Mar 2016 12:30:23 +0100 -Subject: [PATCH 05/17] Use SHA256 hash type by default - ---- - conf/master | 2 +- - conf/minion | 2 +- - conf/proxy | 2 +- - 3 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/conf/master b/conf/master -index ee98e51..6ef1c10 100644 ---- a/conf/master -+++ b/conf/master -@@ -509,7 +509,7 @@ syndic_user: salt - # - # Prior to changing this value, the master should be stopped and all Salt - # caches should be cleared. --#hash_type: md5 -+hash_type: sha256 - - # The buffer size in the file server can be adjusted here: - #file_buffer_size: 1048576 -diff --git a/conf/minion b/conf/minion -index 9bc9408..07b706f 100644 ---- a/conf/minion -+++ b/conf/minion -@@ -559,7 +559,7 @@ - # - # Warning: Prior to changing this value, the minion should be stopped and all - # Salt caches should be cleared. --#hash_type: sha256 -+hash_type: sha256 - - # The Salt pillar is searched for locally if file_client is set to local. If - # this is the case, and pillar data is defined, then the pillar_roots need to -diff --git a/conf/proxy b/conf/proxy -index 4e5cc34..8e5dd2d 100644 ---- a/conf/proxy -+++ b/conf/proxy -@@ -445,7 +445,7 @@ - # - # Warning: Prior to changing this value, the minion should be stopped and all - # Salt caches should be cleared. --#hash_type: sha256 -+hash_type: sha256 - - # The Salt pillar is searched for locally if file_client is set to local. If - # this is the case, and pillar data is defined, then the pillar_roots need to --- -2.10.1 - diff --git a/0006-Create-salt-proxy-instantiated-service-file.patch b/0006-Create-salt-proxy-instantiated-service-file.patch deleted file mode 100644 index e794f3e..0000000 --- a/0006-Create-salt-proxy-instantiated-service-file.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 57f9c9cb438f13475ca437252770dc8d365d96cd Mon Sep 17 00:00:00 2001 -From: Christian McHugh -Date: Thu, 10 Mar 2016 13:25:01 -0600 -Subject: [PATCH 06/17] Create salt-proxy instantiated service file - -Add a systemd service file for salt-proxy. - -Instantiate a new proxy service with proxyid=p8000: -# systemctl enable salt-proxy\@p8000.service -# systemctl start salt-proxy\@p8000.service ---- - pkg/salt-proxy@.service | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - create mode 100644 pkg/salt-proxy@.service - -diff --git a/pkg/salt-proxy@.service b/pkg/salt-proxy@.service -new file mode 100644 -index 0000000..f97120a ---- /dev/null -+++ b/pkg/salt-proxy@.service -@@ -0,0 +1,12 @@ -+[Unit] -+Description=salt-proxy service -+After=network.target -+ -+[Service] -+ExecStart=/usr/bin/salt-proxy --proxyid=%I -+Type=simple -+Restart=on-failure -+RestartSec=5s -+ -+[Install] -+WantedBy=multi-user.target --- -2.10.1 - diff --git a/0008-snapper-execution-module.patch b/0008-snapper-execution-module.patch deleted file mode 100644 index 70ca353..0000000 --- a/0008-snapper-execution-module.patch +++ /dev/null @@ -1,1278 +0,0 @@ -From 7ea96fd6c6b9b7c5a461aae7f5b8487e54d41dc6 Mon Sep 17 00:00:00 2001 -From: Pablo Suarez Hernandez -Date: Mon, 4 Jul 2016 16:26:33 +0100 -Subject: [PATCH 08/17] snapper execution module - -snapper state module - -snapper module unit tests - -some pylint fixes - -more unit tests - -Fix for snapper.diff when files are created or deleted - -fix diff unit test while creating text file - -passing *args and **kwargs to function when snapper.run - -unit test for snapper.diff with binary file - -load snapper state only if snapper module is loaded - -Fix for _get_jid_snapshots if snapshots doesn't exist - -pylint fixes - -pylint: some fixes - -Variable renaming. Pylint fixes - -Fix in inline comments - -Fix for pylint: W1699 - -some fixes and comments improvement - -Prevent module failing if Snapper does not exist in D-Bus - -Added function for baseline creation - -Allow tag reference for baseline_snapshot state ---- - salt/modules/snapper.py | 687 +++++++++++++++++++++++++++++++++++++ - salt/states/snapper.py | 195 +++++++++++ - tests/unit/modules/snapper_test.py | 324 +++++++++++++++++ - 3 files changed, 1206 insertions(+) - create mode 100644 salt/modules/snapper.py - create mode 100644 salt/states/snapper.py - create mode 100644 tests/unit/modules/snapper_test.py - -diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py -new file mode 100644 -index 0000000..9a73820 ---- /dev/null -+++ b/salt/modules/snapper.py -@@ -0,0 +1,687 @@ -+# -*- coding: utf-8 -*- -+''' -+Module to manage filesystem snapshots with snapper -+ -+:codeauthor: Duncan Mac-Vicar P. -+:codeauthor: Pablo Suárez Hernández -+ -+:depends: ``dbus`` Python module. -+:depends: ``snapper`` http://snapper.io, available in most distros -+:maturity: new -+:platform: Linux -+''' -+ -+from __future__ import absolute_import -+ -+import logging -+import os -+import time -+import difflib -+from pwd import getpwuid -+ -+from salt.exceptions import CommandExecutionError -+import salt.utils -+ -+ -+try: -+ import dbus # pylint: disable=wrong-import-order -+ HAS_DBUS = True -+except ImportError: -+ HAS_DBUS = False -+ -+ -+DBUS_STATUS_MAP = { -+ 1: "created", -+ 2: "deleted", -+ 4: "type changed", -+ 8: "modified", -+ 16: "permission changed", -+ 32: "owner changed", -+ 64: "group changed", -+ 128: "extended attributes changed", -+ 256: "ACL info changed", -+} -+ -+SNAPPER_DBUS_OBJECT = 'org.opensuse.Snapper' -+SNAPPER_DBUS_PATH = '/org/opensuse/Snapper' -+SNAPPER_DBUS_INTERFACE = 'org.opensuse.Snapper' -+ -+log = logging.getLogger(__name__) # pylint: disable=invalid-name -+ -+bus = None # pylint: disable=invalid-name -+snapper = None # pylint: disable=invalid-name -+ -+if HAS_DBUS: -+ bus = dbus.SystemBus() # pylint: disable=invalid-name -+ if SNAPPER_DBUS_OBJECT in bus.list_activatable_names(): -+ snapper = dbus.Interface(bus.get_object(SNAPPER_DBUS_OBJECT, # pylint: disable=invalid-name -+ SNAPPER_DBUS_PATH), -+ dbus_interface=SNAPPER_DBUS_INTERFACE) -+ -+ -+def __virtual__(): -+ if not HAS_DBUS: -+ return (False, 'The snapper module cannot be loaded:' -+ ' missing python dbus module') -+ elif not snapper: -+ return (False, 'The snapper module cannot be loaded:' -+ ' missing snapper') -+ return 'snapper' -+ -+ -+def _snapshot_to_data(snapshot): -+ ''' -+ Returns snapshot data from a D-Bus response. -+ -+ A snapshot D-Bus response is a dbus.Struct containing the -+ information related to a snapshot: -+ -+ [id, type, pre_snapshot, timestamp, user, description, -+ cleanup_algorithm, userdata] -+ -+ id: dbus.UInt32 -+ type: dbus.UInt16 -+ pre_snapshot: dbus.UInt32 -+ timestamp: dbus.Int64 -+ user: dbus.UInt32 -+ description: dbus.String -+ cleaup_algorithm: dbus.String -+ userdata: dbus.Dictionary -+ ''' -+ data = {} -+ -+ data['id'] = snapshot[0] -+ data['type'] = ['single', 'pre', 'post'][snapshot[1]] -+ if data['type'] == 'post': -+ data['pre'] = snapshot[2] -+ -+ if snapshot[3] != -1: -+ data['timestamp'] = snapshot[3] -+ else: -+ data['timestamp'] = int(time.time()) -+ -+ data['user'] = getpwuid(snapshot[4])[0] -+ data['description'] = snapshot[5] -+ data['cleanup'] = snapshot[6] -+ -+ data['userdata'] = {} -+ for key, value in snapshot[7].items(): -+ data['userdata'][key] = value -+ -+ return data -+ -+ -+def _dbus_exception_to_reason(exc, args): -+ ''' -+ Returns a error message from a snapper DBusException -+ ''' -+ error = exc.get_dbus_name() -+ if error == 'error.unknown_config': -+ return "Unknown configuration '{0}'".format(args['config']) -+ elif error == 'error.illegal_snapshot': -+ return 'Invalid snapshot' -+ else: -+ return exc.get_dbus_name() -+ -+ -+def list_snapshots(config='root'): -+ ''' -+ List available snapshots -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.list_snapshots config=myconfig -+ ''' -+ try: -+ snapshots = snapper.ListSnapshots(config) -+ return [_snapshot_to_data(s) for s in snapshots] -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while listing snapshots: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def get_snapshot(number=0, config='root'): -+ ''' -+ Get detailed information about a given snapshot -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.get_snapshot 1 -+ ''' -+ try: -+ snapshot = snapper.GetSnapshot(config, int(number)) -+ return _snapshot_to_data(snapshot) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while retrieving snapshot: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def list_configs(): -+ ''' -+ List all available configs -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.list_configs -+ ''' -+ try: -+ configs = snapper.ListConfigs() -+ return dict((config[0], config[2]) for config in configs) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while listing configurations: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def _config_filter(value): -+ if isinstance(value, bool): -+ return 'yes' if value else 'no' -+ return value -+ -+ -+def set_config(name='root', **kwargs): -+ ''' -+ Set configuration values -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.set_config SYNC_ACL=True -+ -+ Keys are case insensitive as they will be always uppercased to -+ snapper convention. The above example is equivalent to: -+ -+ .. code-block:: bash -+ salt '*' snapper.set_config sync_acl=True -+ ''' -+ try: -+ data = dict((k.upper(), _config_filter(v)) for k, v in -+ kwargs.items() if not k.startswith('__')) -+ snapper.SetConfig(name, data) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while setting configuration {0}: {1}' -+ .format(name, _dbus_exception_to_reason(exc, locals())) -+ ) -+ return True -+ -+ -+def _get_last_snapshot(config='root'): -+ ''' -+ Returns the last existing created snapshot -+ ''' -+ snapshot_list = sorted(list_snapshots(config), key=lambda x: x['id']) -+ return snapshot_list[-1] -+ -+ -+def status_to_string(dbus_status): -+ ''' -+ Converts a numeric dbus snapper status into a string -+ ''' -+ status_tuple = ( -+ dbus_status & 0b000000001, dbus_status & 0b000000010, dbus_status & 0b000000100, -+ dbus_status & 0b000001000, dbus_status & 0b000010000, dbus_status & 0b000100000, -+ dbus_status & 0b001000000, dbus_status & 0b010000000, dbus_status & 0b100000000 -+ ) -+ -+ return [DBUS_STATUS_MAP[status] for status in status_tuple if status] -+ -+ -+def get_config(name='root'): -+ ''' -+ Retrieves all values from a given configuration -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.get_config -+ ''' -+ try: -+ config = snapper.GetConfig(name) -+ return config -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while retrieving configuration: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def create_snapshot(config='root', snapshot_type='single', pre_number=None, -+ description=None, cleanup_algorithm='number', userdata=None, -+ **kwargs): -+ ''' -+ Creates an snapshot -+ -+ config -+ Configuration name. -+ snapshot_type -+ Specifies the type of the new snapshot. Possible values are -+ single, pre and post. -+ pre_number -+ For post snapshots the number of the pre snapshot must be -+ provided. -+ description -+ Description for the snapshot. If not given, the salt job will be used. -+ cleanup_algorithm -+ Set the cleanup algorithm for the snapshot. -+ -+ number -+ Deletes old snapshots when a certain number of snapshots -+ is reached. -+ timeline -+ Deletes old snapshots but keeps a number of hourly, -+ daily, weekly, monthly and yearly snapshots. -+ empty-pre-post -+ Deletes pre/post snapshot pairs with empty diffs. -+ userdata -+ Set userdata for the snapshot (key-value pairs). -+ -+ Returns the number of the created snapshot. -+ -+ .. code-block:: bash -+ salt '*' snapper.create_snapshot -+ ''' -+ if not userdata: -+ userdata = {} -+ -+ jid = kwargs.get('__pub_jid') -+ if description is None and jid is not None: -+ description = 'salt job {0}'.format(jid) -+ -+ if jid is not None: -+ userdata['salt_jid'] = jid -+ -+ new_nr = None -+ try: -+ if snapshot_type == 'single': -+ new_nr = snapper.CreateSingleSnapshot(config, description, -+ cleanup_algorithm, userdata) -+ elif snapshot_type == 'pre': -+ new_nr = snapper.CreatePreSnapshot(config, description, -+ cleanup_algorithm, userdata) -+ elif snapshot_type == 'post': -+ if pre_number is None: -+ raise CommandExecutionError( -+ "pre snapshot number 'pre_number' needs to be" -+ "specified for snapshots of the 'post' type") -+ new_nr = snapper.CreatePostSnapshot(config, pre_number, description, -+ cleanup_algorithm, userdata) -+ else: -+ raise CommandExecutionError( -+ "Invalid snapshot type '{0}'", format(snapshot_type)) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while listing changed files: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ return new_nr -+ -+ -+def _get_num_interval(config, num_pre, num_post): -+ ''' -+ Returns numerical interval based on optionals num_pre, num_post values -+ ''' -+ post = int(num_post) if num_post else 0 -+ pre = int(num_pre) if num_pre is not None else _get_last_snapshot(config)['id'] -+ return pre, post -+ -+ -+def _is_text_file(filename): -+ ''' -+ Checks if a file is a text file -+ ''' -+ type_of_file = os.popen('file -bi {0}'.format(filename), 'r').read() -+ return type_of_file.startswith('text') -+ -+ -+def run(function, *args, **kwargs): -+ ''' -+ Runs a function from an execution module creating pre and post snapshots -+ and associating the salt job id with those snapshots for easy undo and -+ cleanup. -+ -+ function -+ Salt function to call. -+ -+ config -+ Configuration name. (default: "root") -+ -+ description -+ A description for the snapshots. (default: None) -+ -+ userdata -+ Data to include in the snapshot metadata. (default: None) -+ -+ cleanup_algorithm -+ Snapper cleanup algorithm. (default: "number") -+ -+ `*args` -+ args for the function to call. (default: None) -+ -+ `**kwargs` -+ kwargs for the function to call (default: None) -+ -+ .. code-block:: bash -+ salt '*' snapper.run file.append args='["/etc/motd", "some text"]' -+ -+ This would run append text to /etc/motd using the file.append -+ module, and will create two snapshots, pre and post with the associated -+ metadata. The jid will be available as salt_jid in the userdata of the -+ snapshot. -+ -+ You can immediately see the changes -+ ''' -+ config = kwargs.pop("config", "root") -+ description = kwargs.pop("description", "snapper.run[{0}]".format(function)) -+ cleanup_algorithm = kwargs.pop("cleanup_algorithm", "number") -+ userdata = kwargs.pop("userdata", {}) -+ -+ func_kwargs = dict((k, v) for k, v in kwargs.items() if not k.startswith('__')) -+ kwargs = dict((k, v) for k, v in kwargs.items() if k.startswith('__')) -+ -+ pre_nr = __salt__['snapper.create_snapshot']( -+ config=config, -+ snapshot_type='pre', -+ description=description, -+ cleanup_algorithm=cleanup_algorithm, -+ userdata=userdata, -+ **kwargs) -+ -+ if function not in __salt__: -+ raise CommandExecutionError( -+ 'function "{0}" does not exist'.format(function) -+ ) -+ -+ try: -+ ret = __salt__[function](*args, **func_kwargs) -+ except CommandExecutionError as exc: -+ ret = "\n".join([str(exc), __salt__[function].__doc__]) -+ -+ __salt__['snapper.create_snapshot']( -+ config=config, -+ snapshot_type='post', -+ pre_number=pre_nr, -+ description=description, -+ cleanup_algorithm=cleanup_algorithm, -+ userdata=userdata, -+ **kwargs) -+ return ret -+ -+ -+def status(config='root', num_pre=None, num_post=None): -+ ''' -+ Returns a comparison between two snapshots -+ -+ config -+ Configuration name. -+ -+ num_pre -+ first snapshot ID to compare. Default is last snapshot -+ -+ num_post -+ last snapshot ID to compare. Default is 0 (current state) -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.status -+ salt '*' snapper.status num_pre=19 num_post=20 -+ ''' -+ try: -+ pre, post = _get_num_interval(config, num_pre, num_post) -+ snapper.CreateComparison(config, int(pre), int(post)) -+ files = snapper.GetFiles(config, int(pre), int(post)) -+ status_ret = {} -+ for file in files: -+ status_ret[file[0]] = {'status': status_to_string(file[1])} -+ return status_ret -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while listing changed files: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def changed_files(config='root', num_pre=None, num_post=None): -+ ''' -+ Returns the files changed between two snapshots -+ -+ config -+ Configuration name. -+ -+ num_pre -+ first snapshot ID to compare. Default is last snapshot -+ -+ num_post -+ last snapshot ID to compare. Default is 0 (current state) -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.changed_files -+ salt '*' snapper.changed_files num_pre=19 num_post=20 -+ ''' -+ return status(config, num_pre, num_post).keys() -+ -+ -+def undo(config='root', files=None, num_pre=None, num_post=None): -+ ''' -+ Undo all file changes that happened between num_pre and num_post, leaving -+ the files into the state of num_pre. -+ -+ .. warning:: -+ If one of the files has changes after num_post, they will be overwriten -+ The snapshots are used to determine the file list, but the current -+ version of the files will be overwritten by the versions in num_pre. -+ -+ You to undo changes between num_pre and the current version of the -+ files use num_post=0. -+ ''' -+ pre, post = _get_num_interval(config, num_pre, num_post) -+ -+ changes = status(config, pre, post) -+ changed = set(changes.keys()) -+ requested = set(files or changed) -+ -+ if not requested.issubset(changed): -+ raise CommandExecutionError( -+ 'Given file list contains files that are not present' -+ 'in the changed filelist: {0}'.format(changed - requested)) -+ -+ cmdret = __salt__['cmd.run']('snapper undochange {0}..{1} {2}'.format( -+ pre, post, ' '.join(requested))) -+ components = cmdret.split(' ') -+ ret = {} -+ for comp in components: -+ key, val = comp.split(':') -+ ret[key] = val -+ return ret -+ -+ -+def _get_jid_snapshots(jid, config='root'): -+ ''' -+ Returns pre/post snapshots made by a given Salt jid -+ -+ Looks for 'salt_jid' entries into snapshots userdata which are created -+ when 'snapper.run' is executed. -+ ''' -+ jid_snapshots = [x for x in list_snapshots(config) if x['userdata'].get("salt_jid") == jid] -+ pre_snapshot = [x for x in jid_snapshots if x['type'] == "pre"] -+ post_snapshot = [x for x in jid_snapshots if x['type'] == "post"] -+ -+ if not pre_snapshot or not post_snapshot: -+ raise CommandExecutionError("Jid '{0}' snapshots not found".format(jid)) -+ -+ return ( -+ pre_snapshot[0]['id'], -+ post_snapshot[0]['id'] -+ ) -+ -+ -+def undo_jid(jid, config='root'): -+ ''' -+ Undo the changes applied by a salt job -+ -+ jid -+ The job id to lookup -+ -+ config -+ Configuration name. -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.undo_jid jid=20160607130930720112 -+ ''' -+ pre_snapshot, post_snapshot = _get_jid_snapshots(jid, config=config) -+ return undo(config, num_pre=pre_snapshot, num_post=post_snapshot) -+ -+ -+def diff(config='root', filename=None, num_pre=None, num_post=None): -+ ''' -+ Returns the differences between two snapshots -+ -+ config -+ Configuration name. -+ -+ filename -+ if not provided the showing differences between snapshots for -+ all "text" files -+ -+ num_pre -+ first snapshot ID to compare. Default is last snapshot -+ -+ num_post -+ last snapshot ID to compare. Default is 0 (current state) -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.diff -+ salt '*' snapper.diff filename=/var/log/snapper.log num_pre=19 num_post=20 -+ ''' -+ try: -+ pre, post = _get_num_interval(config, num_pre, num_post) -+ -+ files = changed_files(config, pre, post) -+ if filename: -+ files = [filename] if filename in files else [] -+ -+ pre_mount = snapper.MountSnapshot(config, pre, False) if pre else "" -+ post_mount = snapper.MountSnapshot(config, post, False) if post else "" -+ -+ files_diff = dict() -+ for filepath in [filepath for filepath in files if not os.path.isdir(filepath)]: -+ pre_file = pre_mount + filepath -+ post_file = post_mount + filepath -+ -+ if os.path.isfile(pre_file): -+ pre_file_exists = True -+ pre_file_content = salt.utils.fopen(pre_file).readlines() -+ else: -+ pre_file_content = [] -+ pre_file_exists = False -+ -+ if os.path.isfile(post_file): -+ post_file_exists = True -+ post_file_content = salt.utils.fopen(post_file).readlines() -+ else: -+ post_file_content = [] -+ post_file_exists = False -+ -+ if _is_text_file(pre_file) or _is_text_file(post_file): -+ files_diff[filepath] = { -+ 'comment': "text file changed", -+ 'diff': ''.join(difflib.unified_diff(pre_file_content, -+ post_file_content, -+ fromfile=pre_file, -+ tofile=post_file))} -+ -+ if pre_file_exists and not post_file_exists: -+ files_diff[filepath]['comment'] = "text file deleted" -+ if not pre_file_exists and post_file_exists: -+ files_diff[filepath]['comment'] = "text file created" -+ -+ elif not _is_text_file(pre_file) and not _is_text_file(post_file): -+ # This is a binary file -+ files_diff[filepath] = {'comment': "binary file changed"} -+ if pre_file_exists: -+ files_diff[filepath]['old_sha256_digest'] = __salt__['hashutil.sha256_digest'](''.join(pre_file_content)) -+ if post_file_exists: -+ files_diff[filepath]['new_sha256_digest'] = __salt__['hashutil.sha256_digest'](''.join(post_file_content)) -+ if post_file_exists and not pre_file_exists: -+ files_diff[filepath]['comment'] = "binary file created" -+ if pre_file_exists and not post_file_exists: -+ files_diff[filepath]['comment'] = "binary file deleted" -+ -+ if pre: -+ snapper.UmountSnapshot(config, pre, False) -+ if post: -+ snapper.UmountSnapshot(config, post, False) -+ return files_diff -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while showing differences between snapshots: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ -+def diff_jid(jid, config='root'): -+ ''' -+ Returns the changes applied by a `jid` -+ -+ jid -+ The job id to lookup -+ -+ config -+ Configuration name. -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.diff_jid jid=20160607130930720112 -+ ''' -+ pre_snapshot, post_snapshot = _get_jid_snapshots(jid, config=config) -+ return diff(config, num_pre=pre_snapshot, num_post=post_snapshot) -+ -+ -+def create_baseline(tag="baseline", config='root'): -+ ''' -+ Creates a snapshot marked as baseline -+ -+ tag -+ Tag name for the baseline -+ -+ config -+ Configuration name. -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.create_baseline -+ salt '*' snapper.create_baseline my_custom_baseline -+ ''' -+ return __salt__['snapper.create_snapshot'](config=config, -+ snapshot_type='single', -+ description="baseline snapshot", -+ cleanup_algorithm="number", -+ userdata={"baseline_tag": tag}) -diff --git a/salt/states/snapper.py b/salt/states/snapper.py -new file mode 100644 -index 0000000..2711550 ---- /dev/null -+++ b/salt/states/snapper.py -@@ -0,0 +1,195 @@ -+# -*- coding: utf-8 -*- -+''' -+Managing implicit state and baselines using snapshots -+===================================================== -+ -+Salt can manage state against explicitly defined state, for example -+if your minion state is defined by: -+ -+.. code-block:: yaml -+ -+ /etc/config_file: -+ file.managed: -+ - source: salt://configs/myconfig -+ -+If someone modifies this file, the next application of the highstate will -+allow the admin to correct this deviation and the file will be corrected. -+ -+Now, what happens if somebody creates a file ``/etc/new_config_file`` and -+deletes ``/etc/important_config_file``? Unless you have a explicit rule, this -+change will go unnoticed. -+ -+The snapper state module allows you to manage state implicitly, in addition -+to explicit rules, in order to define a baseline and iterate with explicit -+rules as they show that they work in production. -+ -+The workflow is: once you have a workin and audited system, you would create -+your baseline snapshot (eg. with ``salt tgt snapper.create_snapshot``) and -+define in your state this baseline using the identifier of the snapshot -+(in this case: 20): -+ -+.. code-block:: yaml -+ -+ my_baseline: -+ snapper.baseline_snapshot: -+ - number: 20 -+ - ignore: -+ - /var/log -+ - /var/cache -+ -+ -+If you have this state, and you haven't done changes to the system since the -+snapshot, and you add a user, the state will show you the changes (including -+full diffs) to ``/etc/passwd``, ``/etc/shadow``, etc if you call it -+with ``test=True`` and will undo all changes if you call it without. -+ -+This allows you to add more explicit state knowing that you are starting from a -+very well defined state, and that you can audit any change that is not part -+of your explicit configuration. -+ -+So after you made this your state, you decided to introduce a change in your -+configuration: -+ -+.. code-block:: yaml -+ -+ my_baseline: -+ snapper.baseline_snapshot: -+ - number: 20 -+ - ignore: -+ - /var/log -+ - /var/cache -+ -+ hosts_entry: -+ file.blockreplace: -+ - name: /etc/hosts -+ - content: 'First line of content' -+ - append_if_not_found: True -+ -+ -+The change in ``/etc/hosts`` will be done after any other change that deviates -+from the specified snapshot are reverted. This could be for example, -+modifications to the ``/etc/passwd`` file or changes in the ``/etc/hosts`` -+that could render your the ``hosts_entry`` rule void or dangerous. -+ -+Once you take a new snapshot and you update the baseline snapshot number to -+include the change in ``/etc/hosts`` the ``hosts_entry`` rule will basically -+do nothing. You are free to leave it there for documentation, to ensure that -+the change is made in case the snapshot is wrong, but if you remove anything -+that comes after the ``snapper.baseline_snapshot`` as it will have no effect: -+ by the moment the state is evaluated, the baseline state was already applied -+and include this change. -+ -+.. warning:: -+ Make sure you specify the baseline state before other rules, otherwise -+ the baseline state will revert all changes if they are not present in -+ the snapshot. -+ -+.. warning:: -+ Do not specify more than one baseline rule as only the last one will -+ affect the result. -+ -+:codeauthor: Duncan Mac-Vicar P. -+:codeauthor: Pablo Suárez Hernández -+ -+:maturity: new -+:platform: Linux -+''' -+ -+from __future__ import absolute_import -+ -+import os -+ -+ -+def __virtual__(): -+ ''' -+ Only load if the snapper module is available in __salt__ -+ ''' -+ return 'snapper' if 'snapper.diff' in __salt__ else False -+ -+ -+def _get_baseline_from_tag(tag): -+ ''' -+ Returns the last created baseline snapshot marked with `tag` -+ ''' -+ last_snapshot = None -+ for snapshot in __salt__['snapper.list_snapshots'](): -+ if tag == snapshot['userdata'].get("baseline_tag"): -+ if not last_snapshot or last_snapshot['timestamp'] < snapshot['timestamp']: -+ last_snapshot = snapshot -+ return last_snapshot -+ -+ -+def baseline_snapshot(name, number=None, tag=None, config='root', ignore=None): -+ ''' -+ Enforces that no file is modified comparing against a previously -+ defined snapshot identified by number. -+ -+ ignore -+ List of files to ignore -+ ''' -+ if not ignore: -+ ignore = [] -+ -+ ret = {'changes': {}, -+ 'comment': '', -+ 'name': name, -+ 'result': True} -+ -+ if number is None and tag is None: -+ ret.update({'result': False, -+ 'comment': 'Snapshot tag or number must be specified'}) -+ return ret -+ -+ if number and tag: -+ ret.update({'result': False, -+ 'comment': 'Cannot use snapshot tag and number at the same time'}) -+ return ret -+ -+ if tag: -+ snapshot = _get_baseline_from_tag(tag) -+ if not snapshot: -+ ret.update({'result': False, -+ 'comment': 'Baseline tag "{0}" not found'.format(tag)}) -+ return ret -+ number = snapshot['id'] -+ -+ status = __salt__['snapper.status']( -+ config, num_pre=number, num_post=0) -+ -+ for target in ignore: -+ if os.path.isfile(target): -+ status.pop(target, None) -+ elif os.path.isdir(target): -+ for target_file in [target_file for target_file in status.keys() if target_file.startswith(target)]: -+ status.pop(target_file, None) -+ -+ for file in status: -+ status[file]['actions'] = status[file].pop("status") -+ -+ # Only include diff for modified files -+ if "modified" in status[file]['actions']: -+ status[file].update(__salt__['snapper.diff'](config, -+ num_pre=0, -+ num_post=number, -+ filename=file)[file]) -+ -+ if __opts__['test'] and status: -+ ret['pchanges'] = ret["changes"] -+ ret['changes'] = {} -+ ret['comment'] = "{0} files changes are set to be undone".format(len(status.keys())) -+ ret['result'] = None -+ elif __opts__['test'] and not status: -+ ret['changes'] = {} -+ ret['comment'] = "Nothing to be done" -+ ret['result'] = True -+ elif not __opts__['test'] and status: -+ undo = __salt__['snapper.undo'](config, num_pre=number, num_post=0, -+ files=status.keys()) -+ ret['changes']['sumary'] = undo -+ ret['changes']['files'] = status -+ ret['result'] = True -+ else: -+ ret['comment'] = "No changes were done" -+ ret['result'] = True -+ -+ return ret -diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py -new file mode 100644 -index 0000000..f27b2ba ---- /dev/null -+++ b/tests/unit/modules/snapper_test.py -@@ -0,0 +1,324 @@ -+# -*- coding: utf-8 -*- -+''' -+Unit tests for the Snapper module -+ -+:codeauthor: Duncan Mac-Vicar P. -+:codeauthor: Pablo Suárez Hernández -+''' -+ -+from __future__ import absolute_import -+ -+from salttesting import TestCase -+from salttesting.mock import ( -+ MagicMock, -+ patch, -+ mock_open, -+) -+ -+from salt.exceptions import CommandExecutionError -+from salttesting.helpers import ensure_in_syspath -+ensure_in_syspath('../../') -+ -+from salt.modules import snapper -+ -+# Globals -+snapper.__salt__ = dict() -+ -+DBUS_RET = { -+ 'ListSnapshots': [ -+ [42, 1, 0, 1457006571, -+ 0, 'Some description', '', -+ {'userdata1': 'userval1', 'salt_jid': '20160607130930720112'}], -+ [43, 2, 42, 1457006572, -+ 0, 'Blah Blah', '', -+ {'userdata2': 'userval2', 'salt_jid': '20160607130930720112'}] -+ ], -+ 'ListConfigs': [ -+ [u'root', u'/', { -+ u'SUBVOLUME': u'/', u'NUMBER_MIN_AGE': u'1800', -+ u'TIMELINE_LIMIT_YEARLY': u'4-10', u'NUMBER_LIMIT_IMPORTANT': u'10', -+ u'FSTYPE': u'btrfs', u'TIMELINE_LIMIT_MONTHLY': u'4-10', -+ u'ALLOW_GROUPS': u'', u'EMPTY_PRE_POST_MIN_AGE': u'1800', -+ u'EMPTY_PRE_POST_CLEANUP': u'yes', u'BACKGROUND_COMPARISON': u'yes', -+ u'TIMELINE_LIMIT_HOURLY': u'4-10', u'ALLOW_USERS': u'', -+ u'TIMELINE_LIMIT_WEEKLY': u'0', u'TIMELINE_CREATE': u'no', -+ u'NUMBER_CLEANUP': u'yes', u'TIMELINE_CLEANUP': u'yes', -+ u'SPACE_LIMIT': u'0.5', u'NUMBER_LIMIT': u'10', -+ u'TIMELINE_MIN_AGE': u'1800', u'TIMELINE_LIMIT_DAILY': u'4-10', -+ u'SYNC_ACL': u'no', u'QGROUP': u'1/0'} -+ ] -+ ], -+ 'GetFiles': [ -+ ['/root/.viminfo', 8], -+ ['/tmp/foo', 52], -+ ['/tmp/foo2', 1], -+ ['/tmp/foo3', 2], -+ ['/var/log/snapper.log', 8], -+ ['/var/cache/salt/minion/extmods/modules/snapper.py', 8], -+ ['/var/cache/salt/minion/extmods/modules/snapper.pyc', 8], -+ ], -+} -+ -+FILE_CONTENT = { -+ '/tmp/foo': { -+ "pre": "dummy text", -+ "post": "another foobar" -+ }, -+ '/tmp/foo2': { -+ "post": "another foobar" -+ } -+} -+ -+MODULE_RET = { -+ 'SNAPSHOTS': [ -+ { -+ 'userdata': {'userdata1': 'userval1', 'salt_jid': '20160607130930720112'}, -+ 'description': 'Some description', 'timestamp': 1457006571, -+ 'cleanup': '', 'user': 'root', 'type': 'pre', 'id': 42 -+ }, -+ { -+ 'pre': 42, -+ 'userdata': {'userdata2': 'userval2', 'salt_jid': '20160607130930720112'}, -+ 'description': 'Blah Blah', 'timestamp': 1457006572, -+ 'cleanup': '', 'user': 'root', 'type': 'post', 'id': 43 -+ } -+ ], -+ 'LISTCONFIGS': { -+ u'root': { -+ u'SUBVOLUME': u'/', u'NUMBER_MIN_AGE': u'1800', -+ u'TIMELINE_LIMIT_YEARLY': u'4-10', u'NUMBER_LIMIT_IMPORTANT': u'10', -+ u'FSTYPE': u'btrfs', u'TIMELINE_LIMIT_MONTHLY': u'4-10', -+ u'ALLOW_GROUPS': u'', u'EMPTY_PRE_POST_MIN_AGE': u'1800', -+ u'EMPTY_PRE_POST_CLEANUP': u'yes', u'BACKGROUND_COMPARISON': u'yes', -+ u'TIMELINE_LIMIT_HOURLY': u'4-10', u'ALLOW_USERS': u'', -+ u'TIMELINE_LIMIT_WEEKLY': u'0', u'TIMELINE_CREATE': u'no', -+ u'NUMBER_CLEANUP': u'yes', u'TIMELINE_CLEANUP': u'yes', -+ u'SPACE_LIMIT': u'0.5', u'NUMBER_LIMIT': u'10', -+ u'TIMELINE_MIN_AGE': u'1800', u'TIMELINE_LIMIT_DAILY': u'4-10', -+ u'SYNC_ACL': u'no', u'QGROUP': u'1/0' -+ } -+ }, -+ 'GETFILES': { -+ '/root/.viminfo': {'status': ['modified']}, -+ '/tmp/foo': {'status': ['type changed', 'permission changed', 'owner changed']}, -+ '/tmp/foo2': {'status': ['created']}, -+ '/tmp/foo3': {'status': ['deleted']}, -+ '/var/log/snapper.log': {'status': ['modified']}, -+ '/var/cache/salt/minion/extmods/modules/snapper.py': {'status': ['modified']}, -+ '/var/cache/salt/minion/extmods/modules/snapper.pyc': {'status': ['modified']}, -+ }, -+ 'DIFF': { -+ '/tmp/foo': { -+ 'comment': 'text file changed', -+ 'diff': "--- /.snapshots/55/snapshot/tmp/foo\n" -+ "+++ /tmp/foo\n" -+ "@@ -1 +1 @@\n" -+ "-dummy text" -+ "+another foobar" -+ }, -+ '/tmp/foo2': { -+ 'comment': 'text file created', -+ 'diff': "--- /.snapshots/55/snapshot/tmp/foo2\n" -+ "+++ /tmp/foo2\n" -+ "@@ -0,0 +1 @@\n" -+ "+another foobar", -+ }, -+ '/tmp/foo3': { -+ 'comment': 'binary file changed', -+ 'old_sha256_digest': 'e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b', -+ 'new_sha256_digest': 'f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa', -+ } -+ } -+} -+ -+ -+class SnapperTestCase(TestCase): -+ def setUp(self): -+ self.dbus_mock = MagicMock() -+ self.DBusExceptionMock = MagicMock() # pylint: disable=invalid-name -+ self.dbus_mock.configure_mock(DBusException=self.DBusExceptionMock) -+ snapper.dbus = self.dbus_mock -+ snapper.snapper = MagicMock() -+ -+ def test__snapshot_to_data(self): -+ data = snapper._snapshot_to_data(DBUS_RET['ListSnapshots'][0]) # pylint: disable=protected-access -+ self.assertEqual(data['id'], 42) -+ self.assertNotIn('pre', data) -+ self.assertEqual(data['type'], 'pre') -+ self.assertEqual(data['user'], 'root') -+ self.assertEqual(data['timestamp'], 1457006571) -+ self.assertEqual(data['description'], 'Some description') -+ self.assertEqual(data['cleanup'], '') -+ self.assertEqual(data['userdata']['userdata1'], 'userval1') -+ -+ @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) -+ def test_list_snapshots(self): -+ self.assertEqual(snapper.list_snapshots(), MODULE_RET["SNAPSHOTS"]) -+ -+ @patch('salt.modules.snapper.snapper.GetSnapshot', MagicMock(return_value=DBUS_RET['ListSnapshots'][0])) -+ def test_get_snapshot(self): -+ self.assertEqual(snapper.get_snapshot(), MODULE_RET["SNAPSHOTS"][0]) -+ self.assertEqual(snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][0]) -+ self.assertNotEqual(snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][1]) -+ -+ @patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])) -+ def test_list_configs(self): -+ self.assertEqual(snapper.list_configs(), MODULE_RET["LISTCONFIGS"]) -+ -+ @patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])) -+ def test_get_config(self): -+ self.assertEqual(snapper.get_config(), DBUS_RET["ListConfigs"][0]) -+ -+ @patch('salt.modules.snapper.snapper.SetConfig', MagicMock()) -+ def test_set_config(self): -+ opts = {'sync_acl': True, 'dummy': False, 'foobar': 1234} -+ self.assertEqual(snapper.set_config(opts), True) -+ -+ def test_status_to_string(self): -+ self.assertEqual(snapper.status_to_string(1), ["created"]) -+ self.assertEqual(snapper.status_to_string(2), ["deleted"]) -+ self.assertEqual(snapper.status_to_string(4), ["type changed"]) -+ self.assertEqual(snapper.status_to_string(8), ["modified"]) -+ self.assertEqual(snapper.status_to_string(16), ["permission changed"]) -+ self.assertListEqual(snapper.status_to_string(24), ["modified", "permission changed"]) -+ self.assertEqual(snapper.status_to_string(32), ["owner changed"]) -+ self.assertEqual(snapper.status_to_string(64), ["group changed"]) -+ self.assertListEqual(snapper.status_to_string(97), ["created", "owner changed", "group changed"]) -+ self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"]) -+ self.assertEqual(snapper.status_to_string(256), ["ACL info changed"]) -+ -+ @patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234)) -+ @patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234)) -+ @patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234)) -+ def test_create_snapshot(self): -+ for snapshot_type in ['pre', 'post', 'single']: -+ opts = { -+ '__pub_jid': 20160607130930720112, -+ 'type': snapshot_type, -+ 'description': 'Test description', -+ 'cleanup_algorithm': 'number', -+ 'pre_number': 23, -+ } -+ self.assertEqual(snapper.create_snapshot(**opts), 1234) -+ -+ @patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})) -+ def test__get_num_interval(self): -+ self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access -+ self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=50), (42, 50)) # pylint: disable=protected-access -+ self.assertEqual(snapper._get_num_interval(config=None, num_pre=42, num_post=50), (42, 50)) # pylint: disable=protected-access -+ -+ def test_run(self): -+ patch_dict = { -+ 'snapper.create_snapshot': MagicMock(return_value=43), -+ 'test.ping': MagicMock(return_value=True), -+ } -+ with patch.dict(snapper.__salt__, patch_dict): -+ self.assertEqual(snapper.run("test.ping"), True) -+ self.assertRaises(CommandExecutionError, snapper.run, "unknown.func") -+ -+ @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))) -+ @patch('salt.modules.snapper.snapper.GetComparison', MagicMock()) -+ @patch('salt.modules.snapper.snapper.GetFiles', MagicMock(return_value=DBUS_RET['GetFiles'])) -+ def test_status(self): -+ self.assertItemsEqual(snapper.status(), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) -+ -+ @patch('salt.modules.snapper.status', MagicMock(return_value=MODULE_RET['GETFILES'])) -+ def test_changed_files(self): -+ self.assertEqual(snapper.changed_files(), MODULE_RET['GETFILES'].keys()) -+ -+ @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))) -+ @patch('salt.modules.snapper.status', MagicMock(return_value=MODULE_RET['GETFILES'])) -+ def test_undo(self): -+ cmd_ret = 'create:0 modify:1 delete:0' -+ with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): -+ module_ret = {'create': '0', 'delete': '0', 'modify': '1'} -+ self.assertEqual(snapper.undo(files=['/tmp/foo']), module_ret) -+ -+ cmd_ret = 'create:1 modify:1 delete:0' -+ with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): -+ module_ret = {'create': '1', 'delete': '0', 'modify': '1'} -+ self.assertEqual(snapper.undo(files=['/tmp/foo', '/tmp/foo2']), module_ret) -+ -+ cmd_ret = 'create:1 modify:1 delete:1' -+ with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): -+ module_ret = {'create': '1', 'delete': '1', 'modify': '1'} -+ self.assertEqual(snapper.undo(files=['/tmp/foo', '/tmp/foo2', '/tmp/foo3']), module_ret) -+ -+ @patch('salt.modules.snapper.list_snapshots', MagicMock(return_value=MODULE_RET['SNAPSHOTS'])) -+ def test__get_jid_snapshots(self): -+ self.assertEqual( -+ snapper._get_jid_snapshots("20160607130930720112"), # pylint: disable=protected-access -+ (MODULE_RET['SNAPSHOTS'][0]['id'], MODULE_RET['SNAPSHOTS'][1]['id']) -+ ) -+ -+ @patch('salt.modules.snapper._get_jid_snapshots', MagicMock(return_value=(42, 43))) -+ @patch('salt.modules.snapper.undo', MagicMock(return_value='create:1 modify:1 delete:1')) -+ def test_undo_jid(self): -+ self.assertEqual(snapper.undo_jid(20160607130930720112), 'create:1 modify:1 delete:1') -+ -+ @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))) -+ @patch('salt.modules.snapper.snapper.MountSnapshot', MagicMock(side_effect=["/.snapshots/55/snapshot", ""])) -+ @patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")) -+ @patch('os.path.isdir', MagicMock(return_value=False)) -+ @patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo2"])) -+ @patch('salt.modules.snapper._is_text_file', MagicMock(return_value=True)) -+ @patch('os.path.isfile', MagicMock(side_effect=[False, True])) -+ @patch('salt.utils.fopen', mock_open(read_data=FILE_CONTENT["/tmp/foo2"]['post'])) -+ def test_diff_text_file(self): -+ self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo2']}) -+ -+ @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(55, 0))) -+ @patch('salt.modules.snapper.snapper.MountSnapshot', MagicMock( -+ side_effect=["/.snapshots/55/snapshot", "", "/.snapshots/55/snapshot", ""])) -+ @patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")) -+ @patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo", "/tmp/foo2"])) -+ @patch('salt.modules.snapper._is_text_file', MagicMock(return_value=True)) -+ @patch('os.path.isfile', MagicMock(side_effect=[True, True, False, True])) -+ @patch('os.path.isdir', MagicMock(return_value=False)) -+ def test_diff_text_files(self): -+ fopen_effect = [ -+ mock_open(read_data=FILE_CONTENT["/tmp/foo"]['pre']).return_value, -+ mock_open(read_data=FILE_CONTENT["/tmp/foo"]['post']).return_value, -+ mock_open(read_data=FILE_CONTENT["/tmp/foo2"]['post']).return_value, -+ ] -+ with patch('salt.utils.fopen') as fopen_mock: -+ fopen_mock.side_effect = fopen_effect -+ module_ret = { -+ "/tmp/foo": MODULE_RET['DIFF']["/tmp/foo"], -+ "/tmp/foo2": MODULE_RET['DIFF']["/tmp/foo2"], -+ } -+ self.assertEqual(snapper.diff(), module_ret) -+ -+ @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(55, 0))) -+ @patch('salt.modules.snapper.snapper.MountSnapshot', MagicMock( -+ side_effect=["/.snapshots/55/snapshot", "", "/.snapshots/55/snapshot", ""])) -+ @patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")) -+ @patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo3"])) -+ @patch('salt.modules.snapper._is_text_file', MagicMock(return_value=False)) -+ @patch('os.path.isfile', MagicMock(side_effect=[True, True])) -+ @patch('os.path.isdir', MagicMock(return_value=False)) -+ @patch.dict(snapper.__salt__, { -+ 'hashutil.sha256_digest': MagicMock(side_effect=[ -+ "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b", -+ "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa", -+ ]) -+ }) -+ def test_diff_binary_files(self): -+ fopen_effect = [ -+ mock_open(read_data="dummy binary").return_value, -+ mock_open(read_data="dummy binary").return_value, -+ ] -+ with patch('salt.utils.fopen') as fopen_mock: -+ fopen_mock.side_effect = fopen_effect -+ module_ret = { -+ "/tmp/foo3": MODULE_RET['DIFF']["/tmp/foo3"], -+ } -+ self.assertEqual(snapper.diff(), module_ret) -+ -+ -+if __name__ == '__main__': -+ from integration import run_tests -+ run_tests(SnapperTestCase, needs_daemon=False) --- -2.10.1 - diff --git a/0011-Fix-snapper_test-for-python26.patch b/0011-Fix-snapper_test-for-python26.patch deleted file mode 100644 index 0bb23cf..0000000 --- a/0011-Fix-snapper_test-for-python26.patch +++ /dev/null @@ -1,113 +0,0 @@ -From 65d1dc94577e9bdbaa6c55f7057a78b2fe6010a7 Mon Sep 17 00:00:00 2001 -From: Justin Anderson -Date: Tue, 23 Aug 2016 15:02:31 -0600 -Subject: [PATCH 11/17] Fix snapper_test for python26 - -* Use assertCountEqual instead of assertItemsEqual for Python 3 -* Skip one Snapper test on 2.6 - There's a slight difference in the diff comparison but we should be - able to catch true failures here with 2.7. ---- - tests/unit/modules/snapper_test.py | 41 ++++++++++++++++++++++++++++++-------- - 1 file changed, 33 insertions(+), 8 deletions(-) - -diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py -index f27b2ba..43f8898 100644 ---- a/tests/unit/modules/snapper_test.py -+++ b/tests/unit/modules/snapper_test.py -@@ -6,19 +6,26 @@ Unit tests for the Snapper module - :codeauthor: Pablo Suárez Hernández - ''' - -+# Import Python libs - from __future__ import absolute_import -+import sys - --from salttesting import TestCase -+# Import Salt Testing libs -+from salttesting import TestCase, skipIf - from salttesting.mock import ( -+ NO_MOCK, -+ NO_MOCK_REASON, - MagicMock, - patch, - mock_open, - ) -- --from salt.exceptions import CommandExecutionError - from salttesting.helpers import ensure_in_syspath -+ - ensure_in_syspath('../../') - -+# Import Salt libs -+import salt.ext.six as six -+from salt.exceptions import CommandExecutionError - from salt.modules import snapper - - # Globals -@@ -123,6 +130,13 @@ MODULE_RET = { - "@@ -0,0 +1 @@\n" - "+another foobar", - }, -+ '/tmp/foo26': { -+ 'comment': 'text file created', -+ 'diff': "--- /.snapshots/55/snapshot/tmp/foo2 \n" -+ "+++ /tmp/foo2 \n" -+ "@@ -1,0 +1,1 @@\n" -+ "+another foobar", -+ }, - '/tmp/foo3': { - 'comment': 'binary file changed', - 'old_sha256_digest': 'e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b', -@@ -132,6 +146,7 @@ MODULE_RET = { - } - - -+@skipIf(NO_MOCK, NO_MOCK_REASON) - class SnapperTestCase(TestCase): - def setUp(self): - self.dbus_mock = MagicMock() -@@ -220,10 +235,16 @@ class SnapperTestCase(TestCase): - @patch('salt.modules.snapper.snapper.GetComparison', MagicMock()) - @patch('salt.modules.snapper.snapper.GetFiles', MagicMock(return_value=DBUS_RET['GetFiles'])) - def test_status(self): -- self.assertItemsEqual(snapper.status(), MODULE_RET['GETFILES']) -- self.assertItemsEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) -- self.assertItemsEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) -- self.assertItemsEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) -+ if six.PY3: -+ self.assertCountEqual(snapper.status(), MODULE_RET['GETFILES']) -+ self.assertCountEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) -+ self.assertCountEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) -+ self.assertCountEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) -+ else: -+ self.assertItemsEqual(snapper.status(), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) -+ self.assertItemsEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) - - @patch('salt.modules.snapper.status', MagicMock(return_value=MODULE_RET['GETFILES'])) - def test_changed_files(self): -@@ -268,7 +289,10 @@ class SnapperTestCase(TestCase): - @patch('os.path.isfile', MagicMock(side_effect=[False, True])) - @patch('salt.utils.fopen', mock_open(read_data=FILE_CONTENT["/tmp/foo2"]['post'])) - def test_diff_text_file(self): -- self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo2']}) -+ if sys.version_info < (2, 7): -+ self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo26']}) -+ else: -+ self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo2']}) - - @patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(55, 0))) - @patch('salt.modules.snapper.snapper.MountSnapshot', MagicMock( -@@ -278,6 +302,7 @@ class SnapperTestCase(TestCase): - @patch('salt.modules.snapper._is_text_file', MagicMock(return_value=True)) - @patch('os.path.isfile', MagicMock(side_effect=[True, True, False, True])) - @patch('os.path.isdir', MagicMock(return_value=False)) -+ @skipIf(sys.version_info < (2, 7), 'Python 2.7 required to compare diff properly') - def test_diff_text_files(self): - fopen_effect = [ - mock_open(read_data=FILE_CONTENT["/tmp/foo"]['pre']).return_value, --- -2.10.1 - diff --git a/0012-Fix-pkg.upgrade-for-zypper.patch b/0012-Fix-pkg.upgrade-for-zypper.patch deleted file mode 100644 index 3fc855b..0000000 --- a/0012-Fix-pkg.upgrade-for-zypper.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 956c7636a7cd83de6afb80b1cd9f0621419497c5 Mon Sep 17 00:00:00 2001 -From: "C. R. Oldham" -Date: Wed, 21 Sep 2016 20:05:33 -0600 -Subject: [PATCH 12/17] Fix pkg.upgrade for zypper - ---- - salt/modules/zypper.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py -index b2a4e0b..49ce5ec 100644 ---- a/salt/modules/zypper.py -+++ b/salt/modules/zypper.py -@@ -1146,7 +1146,7 @@ def upgrade(refresh=True, - if dryrun: - # Creates a solver test case for debugging. - log.info('Executing debugsolver and performing a dry-run dist-upgrade') -- __zypper__.noraise.call(*cmd_update + ['--debug-solver']) -+ __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver']) - - if fromrepo: - for repo in fromrepo: -@@ -1167,6 +1167,7 @@ def upgrade(refresh=True, - old = list_pkgs() - - __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update) -+ - if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES: - ret['result'] = False - else: --- -2.10.1 - diff --git a/0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch b/0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch deleted file mode 100644 index 1cf8546..0000000 --- a/0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 84bacc068ff10b33b5bd984459d6cfb73b1bf2c9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Fri, 30 Sep 2016 13:06:52 +0100 -Subject: [PATCH 13/17] Setting up OS grains for SLES Expanded Support (SUSE's - Red Hat compatible platform) - -core.py: quote style fixed ---- - salt/grains/core.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/salt/grains/core.py b/salt/grains/core.py -index 867924a..12a2959 100644 ---- a/salt/grains/core.py -+++ b/salt/grains/core.py -@@ -1038,6 +1038,7 @@ _OS_NAME_MAP = { - 'manjaro': 'Manjaro', - 'antergos': 'Antergos', - 'sles': 'SUSE', -+ 'slesexpand': 'RES', - 'linuxmint': 'Mint', - } - -@@ -1057,6 +1058,7 @@ _OS_FAMILY_MAP = { - 'OEL': 'RedHat', - 'XCP': 'RedHat', - 'XenServer': 'RedHat', -+ 'RES': 'RedHat', - 'Mandrake': 'Mandriva', - 'ESXi': 'VMWare', - 'Mint': 'Debian', --- -2.10.1 - diff --git a/0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch b/0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch deleted file mode 100644 index 7f00299..0000000 --- a/0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 3da08fe5187b0a7279b0fe6d24f969bfe1026e7a Mon Sep 17 00:00:00 2001 -From: Joerg Reuter -Date: Wed, 12 Oct 2016 13:02:38 +0200 -Subject: [PATCH 14/17] - acl.delfacl: fix position of -X option to setfacl - -Adjust linux_acl unit test argument ordering ---- - salt/modules/linux_acl.py | 4 +++- - tests/unit/modules/linux_acl_test.py | 2 +- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/salt/modules/linux_acl.py b/salt/modules/linux_acl.py -index c23bc8c..0f5c874 100644 ---- a/salt/modules/linux_acl.py -+++ b/salt/modules/linux_acl.py -@@ -250,10 +250,12 @@ def delfacl(acl_type, acl_name='', *args, **kwargs): - - _raise_on_no_files(*args) - -- cmd = 'setfacl -x' -+ cmd = 'setfacl' - if recursive: - cmd += ' -R' - -+ cmd += ' -x' -+ - cmd = '{0} {1}:{2}'.format(cmd, _acl_prefix(acl_type), acl_name) - - for dentry in args: -diff --git a/tests/unit/modules/linux_acl_test.py b/tests/unit/modules/linux_acl_test.py -index 1398636..36d3202 100644 ---- a/tests/unit/modules/linux_acl_test.py -+++ b/tests/unit/modules/linux_acl_test.py -@@ -193,4 +193,4 @@ class LinuxAclTestCase(TestCase): - - def test_delfacl__recursive_w_multiple_args(self): - linux_acl.delfacl(*(self.default_user_acl[:-1] + self.files), recursive=True) -- self.cmdrun.assert_called_once_with('setfacl -x -R ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.files), python_shell=False) -+ self.cmdrun.assert_called_once_with('setfacl -R -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.files), python_shell=False) --- -2.10.1 - diff --git a/0016-Fix-pkg.latest_version-when-latest-already-installed.patch b/0016-Fix-pkg.latest_version-when-latest-already-installed.patch deleted file mode 100644 index 8f5c7cd..0000000 --- a/0016-Fix-pkg.latest_version-when-latest-already-installed.patch +++ /dev/null @@ -1,25 +0,0 @@ -From b784cb1d2dd3b190e8664c2a40e8d3c55ad4ad03 Mon Sep 17 00:00:00 2001 -From: Mihai Dinca -Date: Fri, 21 Oct 2016 16:25:04 +0200 -Subject: [PATCH 16/17] Fix pkg.latest_version when latest already installed - ---- - salt/states/pkg.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/salt/states/pkg.py b/salt/states/pkg.py -index 8492de7..9cecc07 100644 ---- a/salt/states/pkg.py -+++ b/salt/states/pkg.py -@@ -1679,7 +1679,7 @@ def latest( - targets = {} - problems = [] - for pkg in desired_pkgs: -- if not avail[pkg]: -+ if not avail.get(pkg): - # Package either a) is up-to-date, or b) does not exist - if not cur[pkg]: - # Package does not exist --- -2.10.1 - diff --git a/0017-Including-resolver-params-for-Zypper-debug-solver.patch b/0017-Including-resolver-params-for-Zypper-debug-solver.patch deleted file mode 100644 index 781a2a6..0000000 --- a/0017-Including-resolver-params-for-Zypper-debug-solver.patch +++ /dev/null @@ -1,74 +0,0 @@ -From e572a2774e186681820c6e9fc6df65516eba2abf Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Mon, 31 Oct 2016 16:15:36 +0000 -Subject: [PATCH 17/17] Including resolver params for Zypper debug-solver - -Now '--no-allow-vendor-change' and '--from' parameters are included -into the zypper --debug-solver call before performing a dry-run dist-upgrade. ---- - salt/modules/zypper.py | 15 ++++++++------- - tests/unit/modules/zypper_test.py | 7 +++++++ - 2 files changed, 15 insertions(+), 7 deletions(-) - -diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py -index 49ce5ec..98451bf 100644 ---- a/salt/modules/zypper.py -+++ b/salt/modules/zypper.py -@@ -1136,6 +1136,10 @@ def upgrade(refresh=True, - - cmd_update = (['dist-upgrade'] if dist_upgrade else ['update']) + ['--auto-agree-with-licenses'] - -+ if skip_verify: -+ # The '--no-gpg-checks' needs to be placed before the Zypper command. -+ cmd_update.insert(0, '--no-gpg-checks') -+ - if refresh: - refresh_db() - -@@ -1143,11 +1147,6 @@ def upgrade(refresh=True, - cmd_update.append('--dry-run') - - if dist_upgrade: -- if dryrun: -- # Creates a solver test case for debugging. -- log.info('Executing debugsolver and performing a dry-run dist-upgrade') -- __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver']) -- - if fromrepo: - for repo in fromrepo: - cmd_update.extend(['--from', repo]) -@@ -1161,8 +1160,10 @@ def upgrade(refresh=True, - else: - log.warn('Disabling vendor changes is not supported on this Zypper version') - -- if skip_verify: -- cmd_update.append('--no-gpg-checks') -+ if dryrun: -+ # Creates a solver test case for debugging. -+ log.info('Executing debugsolver and performing a dry-run dist-upgrade') -+ __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver']) - - old = list_pkgs() - -diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py -index fe170b4..56f68b6 100644 ---- a/tests/unit/modules/zypper_test.py -+++ b/tests/unit/modules/zypper_test.py -@@ -359,6 +359,13 @@ class ZypperTestCase(TestCase): - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run') - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--debug-solver') - -+ with patch('salt.modules.zypper.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])): -+ ret = zypper.upgrade(dist_upgrade=True, dryrun=True, fromrepo=["Dummy", "Dummy2"], novendorchange=True) -+ self.assertTrue(ret['result']) -+ self.assertDictEqual(ret['changes'], {}) -+ zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change') -+ zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change', '--debug-solver') -+ - with patch('salt.modules.zypper.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])): - ret = zypper.upgrade(dist_upgrade=True, fromrepo=["Dummy", "Dummy2"], novendorchange=True) - self.assertTrue(ret['result']) --- -2.10.1 - diff --git a/add-a-salt-minion-service-control-file.patch b/add-a-salt-minion-service-control-file.patch new file mode 100644 index 0000000..28df4b8 --- /dev/null +++ b/add-a-salt-minion-service-control-file.patch @@ -0,0 +1,35 @@ +From 69eeaf17252a2912ed33f2160c14282c2ff703bc Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Wed, 18 Jan 2017 15:38:53 +0100 +Subject: [PATCH] Add a salt-minion service control file + +--- + pkg/suse/salt-minion.service | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 pkg/suse/salt-minion.service + +diff --git a/pkg/suse/salt-minion.service b/pkg/suse/salt-minion.service +new file mode 100644 +index 0000000000..1dbaa77755 +--- /dev/null ++++ b/pkg/suse/salt-minion.service +@@ -0,0 +1,15 @@ ++[Unit] ++Description=The Salt Minion ++After=network.target ++ ++[Service] ++Type=notify ++NotifyAccess=all ++LimitNOFILE=8192 ++ExecStart=/usr/bin/salt-minion ++KillMode=process ++Restart=on-failure ++RestartSec=15 ++ ++[Install] ++WantedBy=multi-user.target +-- +2.11.0 + + diff --git a/add-buildargs-option-to-dockerng.build.patch b/add-buildargs-option-to-dockerng.build.patch new file mode 100644 index 0000000..8b76c26 --- /dev/null +++ b/add-buildargs-option-to-dockerng.build.patch @@ -0,0 +1,48 @@ +From 409e1835e0f26295212d1d148d1e7b42cf7dc6b0 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Thu, 19 Jan 2017 15:39:10 +0100 +Subject: [PATCH] add buildargs option to dockerng.build + +--- + salt/modules/dockerng.py | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py +index 6c31c18289..87000e3846 100644 +--- a/salt/modules/dockerng.py ++++ b/salt/modules/dockerng.py +@@ -3553,7 +3553,8 @@ def build(path=None, + rm=True, + api_response=False, + fileobj=None, +- dockerfile=None): ++ dockerfile=None, ++ buildargs=None): + ''' + Builds a docker image from a Dockerfile or a URL + +@@ -3587,6 +3588,10 @@ def build(path=None, + + .. versionadded:: develop + ++ buildargs ++ A dictionary of build arguments provided to the docker build process. ++ ++ + **RETURN DATA** + + A dictionary containing one or more of the following keys: +@@ -3633,7 +3638,8 @@ def build(path=None, + fileobj=fileobj, + rm=rm, + nocache=not cache, +- dockerfile=dockerfile) ++ dockerfile=dockerfile, ++ buildargs=buildargs) + ret = {'Time_Elapsed': time.time() - time_started} + _clear_context() + +-- +2.11.0 + + diff --git a/add-ssh-option-to-salt-ssh.patch b/add-ssh-option-to-salt-ssh.patch new file mode 100644 index 0000000..ec495d7 --- /dev/null +++ b/add-ssh-option-to-salt-ssh.patch @@ -0,0 +1,163 @@ +From 949591bc0a4c0e768c08a2f61c040a1c4f0190ed Mon Sep 17 00:00:00 2001 +From: Matei Albu +Date: Mon, 19 Dec 2016 16:54:52 +0100 +Subject: [PATCH] Add --ssh-option to salt-ssh + +--ssh-option can be used to pass -o options to the ssh client. +(cherry picked from commit 16f21e5) + +Add spaces around = + +Fix salt-ssh err when -ssh-option is missing +--- + salt/client/ssh/__init__.py | 7 ++++++- + salt/client/ssh/shell.py | 19 ++++++++++++++++--- + salt/utils/parsers.py | 18 +++++++++++++----- + 3 files changed, 35 insertions(+), 9 deletions(-) + +diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py +index f0b7a282c0..9ed6be2b75 100644 +--- a/salt/client/ssh/__init__.py ++++ b/salt/client/ssh/__init__.py +@@ -295,6 +295,9 @@ class SSH(object): + 'remote_port_forwards': self.opts.get( + 'ssh_remote_port_forwards' + ), ++ 'ssh_options': self.opts.get( ++ 'ssh_options' ++ ) + } + if self.opts.get('rand_thin_dir'): + self.defaults['thin_dir'] = os.path.join( +@@ -690,6 +693,7 @@ class Single(object): + identities_only=False, + sudo_user=None, + remote_port_forwards=None, ++ ssh_options=None, + **kwargs): + # Get mine setting and mine_functions if defined in kwargs (from roster) + self.mine = mine +@@ -746,7 +750,8 @@ class Single(object): + 'mods': self.mods, + 'identities_only': identities_only, + 'sudo_user': sudo_user, +- 'remote_port_forwards': remote_port_forwards} ++ 'remote_port_forwards': remote_port_forwards, ++ 'ssh_options': ssh_options} + # Pre apply changeable defaults + self.minion_opts = { + 'grains_cache': True, +diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py +index 722ec07633..2884a52c1d 100644 +--- a/salt/client/ssh/shell.py ++++ b/salt/client/ssh/shell.py +@@ -64,7 +64,8 @@ class Shell(object): + mods=None, + identities_only=False, + sudo_user=None, +- remote_port_forwards=None): ++ remote_port_forwards=None, ++ ssh_options=None): + self.opts = opts + self.host = host + self.user = user +@@ -77,6 +78,7 @@ class Shell(object): + self.mods = mods + self.identities_only = identities_only + self.remote_port_forwards = remote_port_forwards ++ self.ssh_options = ssh_options + + def get_error(self, errstr): + ''' +@@ -168,6 +170,12 @@ class Shell(object): + ret.append('-o {0} '.format(option)) + return ''.join(ret) + ++ def _ssh_opts(self): ++ if self.ssh_options: ++ return ' '.join(['-o {0}'.format(opt) ++ for opt in self.ssh_options]) ++ return '' ++ + def _copy_id_str_old(self): + ''' + Return the string to execute ssh-copy-id +@@ -175,11 +183,12 @@ class Shell(object): + if self.passwd: + # Using single quotes prevents shell expansion and + # passwords containing '$' +- return "{0} {1} '{2} -p {3} {4}@{5}'".format( ++ return "{0} {1} '{2} -p {3} {4} {5}@{6}'".format( + 'ssh-copy-id', + '-i {0}.pub'.format(self.priv), + self._passwd_opts(), + self.port, ++ self._ssh_opts(), + self.user, + self.host) + return None +@@ -192,11 +201,12 @@ class Shell(object): + if self.passwd: + # Using single quotes prevents shell expansion and + # passwords containing '$' +- return "{0} {1} {2} -p {3} {4}@{5}".format( ++ return "{0} {1} {2} -p {3} {4} {5}@{6}".format( + 'ssh-copy-id', + '-i {0}.pub'.format(self.priv), + self._passwd_opts(), + self.port, ++ self._ssh_opts(), + self.user, + self.host) + return None +@@ -228,6 +238,9 @@ class Shell(object): + if ssh != 'scp' and self.remote_port_forwards: + command.append(' '.join(['-R {0}'.format(item) + for item in self.remote_port_forwards.split(',')])) ++ if self.ssh_options: ++ command.append(self._ssh_opts()) ++ + command.append(cmd) + + return ' '.join(command) +diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py +index e8f037c3d6..d91c846168 100644 +--- a/salt/utils/parsers.py ++++ b/salt/utils/parsers.py +@@ -2838,11 +2838,11 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta, + help='Pass a JID to be used instead of generating one.' + ) + +- ports_group = optparse.OptionGroup( +- self, 'Port Forwarding Options', +- 'Parameters for setting up SSH port forwarding.' ++ ssh_group = optparse.OptionGroup( ++ self, 'SSH Options', ++ 'Parameters for the SSH client.' + ) +- ports_group.add_option( ++ ssh_group.add_option( + '--remote-port-forwards', + dest='ssh_remote_port_forwards', + help='Setup remote port forwarding using the same syntax as with ' +@@ -2850,7 +2850,15 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta, + 'forwarding definitions will be translated into multiple ' + '-R parameters.' + ) +- self.add_option_group(ports_group) ++ ssh_group.add_option( ++ '--ssh-option', ++ dest='ssh_options', ++ action='append', ++ help='Equivalent to the -o ssh command option. Passes options to ' ++ 'the SSH client in the format used in the client configuration file. ' ++ 'Can be used multiple times.' ++ ) ++ self.add_option_group(ssh_group) + + auth_group = optparse.OptionGroup( + self, 'Authentication Options', +-- +2.11.0 + + diff --git a/add-yum-plugin.patch b/add-yum-plugin.patch new file mode 100644 index 0000000..0d92c8b --- /dev/null +++ b/add-yum-plugin.patch @@ -0,0 +1,116 @@ +From a0523ac82a1dcca7a7c77f9b3816f237f211b94e Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Thu, 29 Sep 2016 17:00:14 +0200 +Subject: [PATCH] Add YUM plugin + +* Add plugin for Yum-Salt integration +* Add configuration for the yumnotify plugin +* Fixes wrong 'enabled' opts for yumnotify plugin +--- + scripts/yum/plugins/README.md | 20 ++++++++++++++ + scripts/yum/plugins/yumnotify.conf | 2 ++ + scripts/yum/plugins/yumnotify.py | 55 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 77 insertions(+) + create mode 100644 scripts/yum/plugins/README.md + create mode 100644 scripts/yum/plugins/yumnotify.conf + create mode 100644 scripts/yum/plugins/yumnotify.py + +diff --git a/scripts/yum/plugins/README.md b/scripts/yum/plugins/README.md +new file mode 100644 +index 0000000000..cb3abd2260 +--- /dev/null ++++ b/scripts/yum/plugins/README.md +@@ -0,0 +1,20 @@ ++## What it is ++ ++Plugin which provides a notification mechanism to Salt, if Yum is ++used outside of it. ++ ++## Installation ++ ++Configuration files are going to: ++ ++ `/etc/yum/pluginconf.d/[name].conf` ++ ++Plugin itself goes to: ++ ++ `/usr/share/yum-plugins/[name].conf` ++ ++## Permissions ++ ++User: root ++Group: root ++Mode: 644 +diff --git a/scripts/yum/plugins/yumnotify.conf b/scripts/yum/plugins/yumnotify.conf +new file mode 100644 +index 0000000000..8e4d76c728 +--- /dev/null ++++ b/scripts/yum/plugins/yumnotify.conf +@@ -0,0 +1,2 @@ ++[main] ++enabled=1 +diff --git a/scripts/yum/plugins/yumnotify.py b/scripts/yum/plugins/yumnotify.py +new file mode 100644 +index 0000000000..268e1e9531 +--- /dev/null ++++ b/scripts/yum/plugins/yumnotify.py +@@ -0,0 +1,55 @@ ++# Copyright (c) 2016 SUSE Linux LLC ++# All Rights Reserved. ++# ++# Author: Bo Maryniuk ++ ++from yum.plugins import TYPE_CORE ++from yum import config ++import os ++import hashlib ++ ++CK_PATH = "/var/cache/salt/minion/rpmdb.cookie" ++RPM_PATH = "/var/lib/rpm/Packages" ++ ++requires_api_version = '2.5' ++plugin_type = TYPE_CORE ++ ++ ++def _get_mtime(): ++ """ ++ Get the modified time of the RPM Database. ++ ++ Returns: ++ Unix ticks ++ """ ++ return os.path.exists(RPM_PATH) and int(os.path.getmtime(RPM_PATH)) or 0 ++ ++ ++def _get_checksum(): ++ """ ++ Get the checksum of the RPM Database. ++ ++ Returns: ++ hexdigest ++ """ ++ digest = hashlib.md5() ++ with open(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 posttrans_hook(conduit): ++ """ ++ Hook after the package installation transaction. ++ ++ :param conduit: ++ :return: ++ """ ++ # Integrate Yum with Salt ++ if 'SALT_RUNNING' not in os.environ: ++ with open(CK_PATH, 'w') as ck_fh: ++ ck_fh.write('{chksum} {mtime}\n'.format(chksum=_get_checksum(), mtime=_get_mtime())) +-- +2.11.0 + + diff --git a/0007-Add-zypp-notify-plugin.patch b/add-zypp-notify-plugin.patch similarity index 96% rename from 0007-Add-zypp-notify-plugin.patch rename to add-zypp-notify-plugin.patch index eb3e969..250c706 100644 --- a/0007-Add-zypp-notify-plugin.patch +++ b/add-zypp-notify-plugin.patch @@ -1,7 +1,7 @@ -From 484b0fa0250b4adc0da34d033ebc5e019e3e1240 Mon Sep 17 00:00:00 2001 +From 2616f36c3a1131a73546449d33d917783f6f1f7b Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Mon, 9 May 2016 10:33:44 +0200 -Subject: [PATCH 07/17] Add zypp-notify plugin +Subject: [PATCH] Add zypp-notify plugin * Add unit test to the libzypp drift detector plugin --- @@ -17,7 +17,7 @@ Subject: [PATCH 07/17] Add zypp-notify plugin diff --git a/scripts/zypper/plugins/commit/README.md b/scripts/zypper/plugins/commit/README.md new file mode 100644 -index 0000000..01c8917 +index 0000000000..01c8917c8e --- /dev/null +++ b/scripts/zypper/plugins/commit/README.md @@ -0,0 +1,3 @@ @@ -26,7 +26,7 @@ index 0000000..01c8917 +Plugins here are required to interact with SUSE Manager in conjunction of SaltStack and Zypper. diff --git a/scripts/zypper/plugins/commit/zyppnotify b/scripts/zypper/plugins/commit/zyppnotify new file mode 100755 -index 0000000..268298b +index 0000000000..268298b108 --- /dev/null +++ b/scripts/zypper/plugins/commit/zyppnotify @@ -0,0 +1,59 @@ @@ -91,7 +91,7 @@ index 0000000..268298b +DriftDetector().main() diff --git a/tests/unit/zypp_plugins_test.py b/tests/unit/zypp_plugins_test.py new file mode 100644 -index 0000000..550403c +index 0000000000..550403cc24 --- /dev/null +++ b/tests/unit/zypp_plugins_test.py @@ -0,0 +1,51 @@ @@ -148,7 +148,7 @@ index 0000000..550403c + 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 +index 0000000000..218f703811 --- /dev/null +++ b/tests/zypp_plugin.py @@ -0,0 +1,64 @@ @@ -217,5 +217,6 @@ index 0000000..218f703 + ''' + self.closed = True -- -2.10.1 +2.11.0 + diff --git a/avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch b/avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch new file mode 100644 index 0000000..d3d2649 --- /dev/null +++ b/avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch @@ -0,0 +1,26 @@ +From b338b21fe340ee4efa0045894315fcf20be1dc49 Mon Sep 17 00:00:00 2001 +From: Silvio Moioli +Date: Wed, 14 Dec 2016 10:33:39 +0100 +Subject: [PATCH] Avoid failures on SLES 12 SP2 because of new systemd + TaskMax limit (bsc#985112) + +--- + pkg/salt-master.service | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/pkg/salt-master.service b/pkg/salt-master.service +index 59be50301a..ecd3edd467 100644 +--- a/pkg/salt-master.service ++++ b/pkg/salt-master.service +@@ -6,6 +6,7 @@ After=network.target + LimitNOFILE=16384 + Type=simple + ExecStart=/usr/bin/salt-master ++TasksMax=infinity + + [Install] + WantedBy=multi-user.target +-- +2.11.0 + + diff --git a/0015-Change-travis-configuration-file-to-use-salt-toaster.patch b/change-travis-configuration-file-to-use-salt-toaster.patch similarity index 93% rename from 0015-Change-travis-configuration-file-to-use-salt-toaster.patch rename to change-travis-configuration-file-to-use-salt-toaster.patch index 92fa25d..ee7e806 100644 --- a/0015-Change-travis-configuration-file-to-use-salt-toaster.patch +++ b/change-travis-configuration-file-to-use-salt-toaster.patch @@ -1,14 +1,14 @@ -From f212d9ae3ecca09808de8a770b1099d94189a2df Mon Sep 17 00:00:00 2001 +From 79f9f4c06813d70cd03ad32c6c8ef8fec1656e88 Mon Sep 17 00:00:00 2001 From: Mihai Dinca Date: Fri, 14 Oct 2016 09:04:47 +0200 -Subject: [PATCH 15/17] Change travis configuration file to use salt-toaster +Subject: [PATCH] Change travis configuration file to use salt-toaster --- .travis.yml | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml -index 7b4c8ce..3101efb 100644 +index 7b4c8ce0e5..3101efb372 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,26 @@ @@ -67,5 +67,6 @@ index 7b4c8ce..3101efb 100644 - on_failure: change +script: make $TARGET PYTEST_CFG=$PYTEST_CFG -- -2.10.1 +2.11.0 + diff --git a/0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch b/check-if-byte-strings-are-properly-encoded-in-utf-8.patch similarity index 80% rename from 0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch rename to check-if-byte-strings-are-properly-encoded-in-utf-8.patch index 7b02e27..24325a2 100644 --- a/0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch +++ b/check-if-byte-strings-are-properly-encoded-in-utf-8.patch @@ -1,7 +1,7 @@ -From 166ee0f5747234024a89a0a68667dec5db98cac7 Mon Sep 17 00:00:00 2001 +From df521307c4bff21ab7891e0086fc4dc8b7c2207c Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Mon, 18 Jan 2016 16:28:48 +0100 -Subject: [PATCH 03/17] Check if byte strings are properly encoded in UTF-8 +Subject: [PATCH] Check if byte strings are properly encoded in UTF-8 Rename keywords arguments variable to a default name. --- @@ -9,10 +9,10 @@ Rename keywords arguments variable to a default name. 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py -index f3e9d68..b2a4e0b 100644 +index 53837e5e73..75e529c3f9 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py -@@ -360,9 +360,9 @@ def info_installed(*names, **kwargs): +@@ -366,9 +366,9 @@ def info_installed(*names, **kwargs): summary, description. :param errors: @@ -25,7 +25,7 @@ index f3e9d68..b2a4e0b 100644 Valid attributes are: ignore, report -@@ -375,7 +375,8 @@ def info_installed(*names, **kwargs): +@@ -381,7 +381,8 @@ def info_installed(*names, **kwargs): salt '*' pkg.info_installed ... salt '*' pkg.info_installed attr=version,vendor salt '*' pkg.info_installed ... attr=version,vendor @@ -35,9 +35,9 @@ index f3e9d68..b2a4e0b 100644 ''' ret = dict() for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): -@@ -386,7 +387,7 @@ def info_installed(*names, **kwargs): - # Check, if string is encoded in a proper UTF-8 - value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore') +@@ -395,7 +396,7 @@ def info_installed(*names, **kwargs): + else: + value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore') if value != value_: - value = kwargs.get('errors') and value_ or 'N/A (invalid UTF-8)' + value = kwargs.get('errors', 'ignore') == 'ignore' and value_ or 'N/A (invalid UTF-8)' @@ -45,5 +45,6 @@ index f3e9d68..b2a4e0b 100644 if key == 'source_rpm': t_nfo['source'] = value -- -2.10.1 +2.11.0 + diff --git a/0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch b/do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch similarity index 67% rename from 0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch rename to do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch index 656897d..79231a9 100644 --- a/0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch +++ b/do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch @@ -1,7 +1,7 @@ -From c52d045e4b292baf95e68b6c61c6162d536cf500 Mon Sep 17 00:00:00 2001 +From 7eeddadbf5ad309045b77762ac9f2f526af83b03 Mon Sep 17 00:00:00 2001 From: Michael Calmer Date: Fri, 4 Mar 2016 09:51:22 +0100 -Subject: [PATCH 04/17] do not generate a date in a comment to prevent rebuilds +Subject: [PATCH] do not generate a date in a comment to prevent rebuilds (bsc#969407) --- @@ -9,10 +9,10 @@ Subject: [PATCH 04/17] do not generate a date in a comment to prevent rebuilds 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py -index c0fd278..dbcccb6 100755 +index f9e9437e71..2356b2ada1 100755 --- a/setup.py +++ b/setup.py -@@ -659,8 +659,7 @@ class Clean(clean): +@@ -667,8 +667,7 @@ class Clean(clean): INSTALL_VERSION_TEMPLATE = '''\ @@ -23,5 +23,6 @@ index c0fd278..dbcccb6 100755 from salt.version import SaltStackVersion -- -2.10.1 +2.11.0 + diff --git a/docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch b/docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch new file mode 100644 index 0000000..80b4f11 --- /dev/null +++ b/docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch @@ -0,0 +1,48 @@ +From 3f950292cf9a3b6d6697e65f986e8559bc396cf2 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Wed, 1 Feb 2017 16:57:13 +0100 +Subject: [PATCH] docker buildargs require minimal dockerpy 1.6.0 and + docker version 1.9.0 + +update version in the docs +--- + salt/modules/dockerng.py | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py +index 87000e3846..f29108f1ba 100644 +--- a/salt/modules/dockerng.py ++++ b/salt/modules/dockerng.py +@@ -28,13 +28,13 @@ to replace references to ``dockerng`` with ``docker``. + Installation Prerequisites + -------------------------- + +-This execution module requires at least version 1.4.0 of both docker-py_ and +-Docker_. docker-py can easily be installed using :py:func:`pip.install +-`: ++This execution module requires at least version 1.6.0 of docker-py_ and ++version 1.9.0 of Docker_. docker-py can easily be installed using ++:py:func:`pip.install `: + + .. code-block:: bash + +- salt myminion pip.install docker-py>=1.4.0 ++ salt myminion pip.install docker-py>=1.6.0 + + .. _docker-py: https://pypi.python.org/pypi/docker-py + .. _Docker: https://www.docker.com/ +@@ -332,8 +332,8 @@ __func_alias__ = { + } + + # Minimum supported versions +-MIN_DOCKER = (1, 6, 0) +-MIN_DOCKER_PY = (1, 4, 0) ++MIN_DOCKER = (1, 9, 0) ++MIN_DOCKER_PY = (1, 6, 0) + + VERSION_RE = r'([\d.]+)' + +-- +2.11.0 + + diff --git a/fix-a-typo-for-master-comments.patch b/fix-a-typo-for-master-comments.patch new file mode 100644 index 0000000..9789cc2 --- /dev/null +++ b/fix-a-typo-for-master-comments.patch @@ -0,0 +1,32 @@ +From 64ddef78a040dde7e547518331dc9e8a98ceb42f Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Thu, 5 Jan 2017 19:09:17 +0100 +Subject: [PATCH] Fix a typo for master comments + +* Disable custom rosters in API +* Pick up a specified roster file from the configured locations +* Explain what it is about and how to configure that +* Add docstring +* Add explanation comment +* Lintfix +--- + conf/master | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/conf/master b/conf/master +index f58eb496bf..3cfdae0a47 100644 +--- a/conf/master ++++ b/conf/master +@@ -405,7 +405,7 @@ syndic_user: salt + # Pass in an alternative location for the salt-ssh roster file + #roster_file: /etc/salt/roster + +-# Define a location for roster files so they can be chosen when using Salt API. ++# Define a locations for roster files so they can be chosen when using Salt API. + # An administrator can place roster files into these locations. Then when + # calling Salt API, parameter 'roster_file' should contain a relative path to + # these locations. That is, "roster_file=/foo/roster" will be resolved as +-- +2.11.0 + + diff --git a/fix-error-parsing.patch b/fix-error-parsing.patch new file mode 100644 index 0000000..1bb6bdf --- /dev/null +++ b/fix-error-parsing.patch @@ -0,0 +1,26 @@ +From 7c459f38d43ef31ededce703fbfb432af1fdcee6 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Sat, 4 Feb 2017 16:05:45 +0100 +Subject: [PATCH] fix error parsing + +--- + salt/modules/dockerng.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py +index f29108f1ba..cbf2a6b397 100644 +--- a/salt/modules/dockerng.py ++++ b/salt/modules/dockerng.py +@@ -1204,7 +1204,7 @@ def _error_detail(data, item): + ''' + err = item['errorDetail'] + if 'code' in err: +- msg = '{1}: {2}'.format( ++ msg = '{0}: {1}'.format( + item['errorDetail']['code'], + item['errorDetail']['message'], + ) +-- +2.11.0 + + diff --git a/fix-leading-dots-on-sanitized-hostname.patch b/fix-leading-dots-on-sanitized-hostname.patch new file mode 100644 index 0000000..af77429 --- /dev/null +++ b/fix-leading-dots-on-sanitized-hostname.patch @@ -0,0 +1,94 @@ +From dadd20bd0db0289ac4ad9f322f03de686b24c5a7 Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Wed, 25 Jan 2017 15:42:08 +0100 +Subject: [PATCH] Fix leading dots on sanitized hostname + +Add unit test + +Fix typo +--- + salt/utils/sanitizers.py | 2 +- + tests/unit/utils/sanitizers_test.py | 57 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 58 insertions(+), 1 deletion(-) + create mode 100644 tests/unit/utils/sanitizers_test.py + +diff --git a/salt/utils/sanitizers.py b/salt/utils/sanitizers.py +index d05d39955d..83f621151a 100644 +--- a/salt/utils/sanitizers.py ++++ b/salt/utils/sanitizers.py +@@ -56,7 +56,7 @@ class InputSanitizer(object): + :param value: + :return: + ''' +- return re.sub(r'[^a-zA-Z0-9.-]', '', InputSanitizer.trim(value)) ++ return re.sub(r'[^a-zA-Z0-9.-]', '', InputSanitizer.trim(value)).strip('.') + + id = hostname + +diff --git a/tests/unit/utils/sanitizers_test.py b/tests/unit/utils/sanitizers_test.py +new file mode 100644 +index 0000000000..e9c333149c +--- /dev/null ++++ b/tests/unit/utils/sanitizers_test.py +@@ -0,0 +1,57 @@ ++# -*- coding: utf-8 -*- ++ ++# Import python libs ++from __future__ import absolute_import ++from salt.ext.six import text_type as text ++ ++# Import Salt Libs ++from salt.utils.sanitizers import clean ++ ++# Import Salt Testing Libs ++from salttesting import TestCase, skipIf ++from salttesting.mock import NO_MOCK, NO_MOCK_REASON ++from salttesting.helpers import ensure_in_syspath ++ ++ensure_in_syspath('../../') ++ ++ ++@skipIf(NO_MOCK, NO_MOCK_REASON) ++class SanitizersTestCase(TestCase): ++ ''' ++ TestCase for sanitizers ++ ''' ++ def test_sanitized_trim(self): ++ ''' ++ Test sanitized input for trimming ++ ''' ++ value = u' sample ' ++ response = clean.trim(value) ++ assert response == 'sample' ++ assert type(response) == text ++ ++ def test_sanitized_filename(self): ++ ''' ++ Test sanitized input for filename ++ ''' ++ value = '/absolute/path/to/the/file.txt' ++ response = clean.filename(value) ++ assert response == 'file.txt' ++ ++ value = '../relative/path/to/the/file.txt' ++ response = clean.filename(value) ++ assert response == 'file.txt' ++ ++ def test_sanitized_hostname(self): ++ ''' ++ Test sanitized input for hostname (id) ++ ''' ++ value = ' ../ ../some/dubious/hostname ' ++ response = clean.hostname(value) ++ assert response == 'somedubioushostname' ++ ++ test_sanitized_id = test_sanitized_hostname ++ ++ ++if __name__ == '__main__': ++ from integration import run_tests ++ run_tests(SanitizersTestCase, needs_daemon=False) +-- +2.11.0 + + diff --git a/0009-fix-salt-summary-to-count-not-responding-minions-cor.patch b/fix-salt-summary-to-count-not-responding-minions-cor.patch similarity index 77% rename from 0009-fix-salt-summary-to-count-not-responding-minions-cor.patch rename to fix-salt-summary-to-count-not-responding-minions-cor.patch index 112d4e8..39e04a0 100644 --- a/0009-fix-salt-summary-to-count-not-responding-minions-cor.patch +++ b/fix-salt-summary-to-count-not-responding-minions-cor.patch @@ -1,7 +1,7 @@ -From e915b72abc48bfa3b3a511c3803153fa44cace23 Mon Sep 17 00:00:00 2001 +From 54fa5d2b6d47d242e98e9a7f4cc597e03084d4d2 Mon Sep 17 00:00:00 2001 From: Michael Calmer Date: Tue, 21 Jun 2016 13:12:48 +0200 -Subject: [PATCH 09/17] fix salt --summary to count not responding minions +Subject: [PATCH] fix salt --summary to count not responding minions correctly (bsc#972311) In case a minion is not responding a dict is returned instead of a string. @@ -10,10 +10,10 @@ In case a minion is not responding a dict is returned instead of a string. 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/cli/salt.py b/salt/cli/salt.py -index 6b18b6f..2fea2f3 100644 +index 1fc9a83508..88ac65e7c2 100644 --- a/salt/cli/salt.py +++ b/salt/cli/salt.py -@@ -264,7 +264,9 @@ class SaltCMD(parsers.SaltCMDOptionParser): +@@ -281,7 +281,9 @@ class SaltCMD(parsers.SaltCMDOptionParser): not_connected_minions = [] failed_minions = [] for each_minion in ret: @@ -25,5 +25,6 @@ index 6b18b6f..2fea2f3 100644 isinstance(minion_ret, string_types) and minion_ret.startswith("Minion did not return") -- -2.10.1 +2.11.0 + diff --git a/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch b/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch new file mode 100644 index 0000000..c5ad91d --- /dev/null +++ b/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch @@ -0,0 +1,34 @@ +From f45b229c9fea4be349484d8d041c735fa78500b5 Mon Sep 17 00:00:00 2001 +From: Frantisek Kobzik +Date: Thu, 12 Jan 2017 16:02:47 +0100 +Subject: [PATCH] Fix timezone handling for rpm installtime (bsc#1017078) + +Previously datetime.fromtimestamp was used. If used without additional +parameters, this method returns date in the local timezone. + +Our code took the result of fromtimestamp, appended 'Z' and returned +this string. This is wrong as 'Z' means UTC (the client code parses this +value as UTC, but it's in fact local time). + +Fixed by using utcfromtimestamp. +--- + salt/modules/rpm.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py +index 68268dea1d..9dace3cb6f 100644 +--- a/salt/modules/rpm.py ++++ b/salt/modules/rpm.py +@@ -575,7 +575,7 @@ def info(*packages, **attr): + # Convert Unix ticks into ISO time format + if key in ['build_date', 'install_date']: + try: +- pkg_data[key] = datetime.datetime.fromtimestamp(int(value)).isoformat() + "Z" ++ pkg_data[key] = datetime.datetime.utcfromtimestamp(int(value)).isoformat() + "Z" + except ValueError: + log.warning('Could not convert "{0}" into Unix time'.format(value)) + continue +-- +2.11.0 + + diff --git a/increasing-timeouts-for-running-integrations-tests.patch b/increasing-timeouts-for-running-integrations-tests.patch new file mode 100644 index 0000000..9d2da84 --- /dev/null +++ b/increasing-timeouts-for-running-integrations-tests.patch @@ -0,0 +1,170 @@ +From 1c22c72afce15585070829054e3460a61947e256 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= + +Date: Mon, 23 Jan 2017 12:10:22 +0000 +Subject: [PATCH] Increasing timeouts for running integrations tests + +--- + tests/integration/__init__.py | 16 ++++++++-------- + tests/integration/runners/state.py | 2 +- + tests/integration/shell/call.py | 6 +++--- + tests/integration/shell/key.py | 2 +- + tests/integration/shell/matcher.py | 2 +- + tests/integration/shell/runner.py | 2 +- + 6 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py +index a37ad61d2e..104566363a 100644 +--- a/tests/integration/__init__.py ++++ b/tests/integration/__init__.py +@@ -801,7 +801,7 @@ class TestDaemon(object): + ) + sys.stdout.flush() + process.start() +- process.wait_until_running(timeout=15) ++ process.wait_until_running(timeout=60) + sys.stdout.write( + '\r{0}\r'.format( + ' ' * getattr(self.parser.options, 'output_columns', PNUM) +@@ -1863,14 +1863,14 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi + except OSError: + os.chdir(INTEGRATION_TEST_DIR) + +- def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=30): # pylint: disable=W0221 ++ def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=60): # pylint: disable=W0221 + ''' + Execute salt + ''' + arg_str = '-c {0} {1}'.format(self.get_config_dir(), arg_str) + return self.run_script('salt', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=timeout) + +- def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, timeout=25): # pylint: disable=W0221 ++ def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, timeout=60): # pylint: disable=W0221 + ''' + Execute salt-ssh + ''' +@@ -1885,7 +1885,7 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi + arg_str, + timeout=timeout, + async_flag=' --async' if async else '') +- return self.run_script('salt-run', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) ++ return self.run_script('salt-run', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) + + def run_run_plus(self, fun, *arg, **kwargs): + ''' +@@ -1932,7 +1932,7 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi + arg_str, + catch_stderr=catch_stderr, + with_retcode=with_retcode, +- timeout=30 ++ timeout=60 + ) + + def run_cp(self, arg_str, with_retcode=False, catch_stderr=False): +@@ -1940,16 +1940,16 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi + Execute salt-cp + ''' + arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str) +- return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) ++ return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) + + def run_call(self, arg_str, with_retcode=False, catch_stderr=False): + ''' + Execute salt-call. + ''' + arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str) +- return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) ++ return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) + +- def run_cloud(self, arg_str, catch_stderr=False, timeout=15): ++ def run_cloud(self, arg_str, catch_stderr=False, timeout=30): + ''' + Execute salt-cloud + ''' +diff --git a/tests/integration/runners/state.py b/tests/integration/runners/state.py +index 07cfe15fe0..3ca74e915b 100644 +--- a/tests/integration/runners/state.py ++++ b/tests/integration/runners/state.py +@@ -109,7 +109,7 @@ class OrchEventTest(integration.ShellCase): + Tests for orchestration events + ''' + def setUp(self): +- self.timeout = 15 ++ self.timeout = 60 + self.master_d_dir = os.path.join(self.get_config_dir(), 'master.d') + try: + os.makedirs(self.master_d_dir) +diff --git a/tests/integration/shell/call.py b/tests/integration/shell/call.py +index 275878f7ca..e444743720 100644 +--- a/tests/integration/shell/call.py ++++ b/tests/integration/shell/call.py +@@ -272,7 +272,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S + '--config-dir {0} --local cmd.run "echo foo"'.format( + config_dir + ), +- timeout=15 ++ timeout=60 + ) + try: + self.assertIn('local:', ret) +@@ -295,7 +295,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S + '--config-dir {0} cmd.run "echo foo"'.format( + config_dir + ), +- timeout=15 ++ timeout=60 + ) + self.assertIn('local:', ret) + finally: +@@ -325,7 +325,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S + '--config-dir {0} cmd.run "echo foo"'.format( + config_dir + ), +- timeout=15, ++ timeout=60, + catch_stderr=True, + with_retcode=True + ) +diff --git a/tests/integration/shell/key.py b/tests/integration/shell/key.py +index bf13fff9a3..aeba671829 100644 +--- a/tests/integration/shell/key.py ++++ b/tests/integration/shell/key.py +@@ -265,7 +265,7 @@ class KeyTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn): + '--config-dir {0} -L'.format( + config_dir + ), +- timeout=15 ++ timeout=60 + ) + try: + self.assertIn('minion', '\n'.join(ret)) +diff --git a/tests/integration/shell/matcher.py b/tests/integration/shell/matcher.py +index e1623ae1b2..2bc8e61220 100644 +--- a/tests/integration/shell/matcher.py ++++ b/tests/integration/shell/matcher.py +@@ -353,7 +353,7 @@ class MatchTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn): + '--config-dir {0} minion test.ping'.format( + config_dir + ), +- timeout=15, ++ timeout=60, + catch_stderr=True, + with_retcode=True + ) +diff --git a/tests/integration/shell/runner.py b/tests/integration/shell/runner.py +index 6d65d537b5..cf7257b93d 100644 +--- a/tests/integration/shell/runner.py ++++ b/tests/integration/shell/runner.py +@@ -109,7 +109,7 @@ class RunTest(integration.ShellCase, testprogram.TestProgramCase, integration.Sh + '--config-dir {0} -d'.format( + config_dir + ), +- timeout=15, ++ timeout=60, + catch_stderr=True, + with_retcode=True + ) +-- +2.11.0 + + diff --git a/prevent-crash-on-unknown-to-the-repo-package.patch b/prevent-crash-on-unknown-to-the-repo-package.patch new file mode 100644 index 0000000..5fa33ed --- /dev/null +++ b/prevent-crash-on-unknown-to-the-repo-package.patch @@ -0,0 +1,26 @@ +From 48a8dd97be9ddef45f249d530b9c7d1a593e5ac0 Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Fri, 27 Jan 2017 14:31:50 +0100 +Subject: [PATCH] Prevent crash on unknown to the repo package + +--- + salt/states/pkg.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/salt/states/pkg.py b/salt/states/pkg.py +index d81f0c00c7..7c3b27b9ac 100644 +--- a/salt/states/pkg.py ++++ b/salt/states/pkg.py +@@ -1909,7 +1909,7 @@ def latest( + for pkg in desired_pkgs: + if not avail.get(pkg): + # Package either a) is up-to-date, or b) does not exist +- if not cur[pkg]: ++ if not cur.get(pkg): + # Package does not exist + msg = 'No information found for \'{0}\'.'.format(pkg) + log.error(msg) +-- +2.11.0 + + diff --git a/prevents-oserror-exception-in-case-path-doesn-t-exis.patch b/prevents-oserror-exception-in-case-path-doesn-t-exis.patch new file mode 100644 index 0000000..a0248b7 --- /dev/null +++ b/prevents-oserror-exception-in-case-path-doesn-t-exis.patch @@ -0,0 +1,28 @@ +From a74360a87e11bbcd65d253426c930769f83361e4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= + +Date: Tue, 14 Feb 2017 16:13:32 +0000 +Subject: [PATCH] Prevents 'OSError' exception in case path doesn't exist + +--- + salt/returners/local_cache.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py +index e2d2979241..d46cb1e70c 100644 +--- a/salt/returners/local_cache.py ++++ b/salt/returners/local_cache.py +@@ -60,6 +60,9 @@ def _walk_through(job_dir): + for top in os.listdir(job_dir): + t_path = os.path.join(job_dir, top) + ++ if not os.path.exists(t_path): ++ continue ++ + for final in os.listdir(t_path): + load_path = os.path.join(t_path, final, LOAD_P) + +-- +2.11.0 + + diff --git a/0010-Run-salt-api-as-user-salt-bsc-990029.patch b/run-salt-api-as-user-salt-bsc-990029.patch similarity index 74% rename from 0010-Run-salt-api-as-user-salt-bsc-990029.patch rename to run-salt-api-as-user-salt-bsc-990029.patch index a39a3f9..bb35b01 100644 --- a/0010-Run-salt-api-as-user-salt-bsc-990029.patch +++ b/run-salt-api-as-user-salt-bsc-990029.patch @@ -1,14 +1,14 @@ -From e955cae5e0ba5fb5348edad433a01f11f1ca887a Mon Sep 17 00:00:00 2001 +From 7bbbd3b6ebaf3988a4f97b905040b56be065f201 Mon Sep 17 00:00:00 2001 From: Michael Calmer Date: Fri, 29 Jul 2016 10:50:21 +0200 -Subject: [PATCH 10/17] Run salt-api as user salt (bsc#990029) +Subject: [PATCH] Run salt-api as user salt (bsc#990029) --- pkg/salt-api.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/salt-api.service b/pkg/salt-api.service -index c3e67d5..9be2cb8 100644 +index c3e67d510c..9be2cb8ee6 100644 --- a/pkg/salt-api.service +++ b/pkg/salt-api.service @@ -3,8 +3,8 @@ Description=The Salt API @@ -23,5 +23,6 @@ index c3e67d5..9be2cb8 100644 ExecStart=/usr/bin/salt-api TimeoutStopSec=3 -- -2.10.1 +2.11.0 + diff --git a/0002-Run-salt-master-as-dedicated-salt-user.patch b/run-salt-master-as-dedicated-salt-user.patch similarity index 84% rename from 0002-Run-salt-master-as-dedicated-salt-user.patch rename to run-salt-master-as-dedicated-salt-user.patch index c8eee96..7de91bb 100644 --- a/0002-Run-salt-master-as-dedicated-salt-user.patch +++ b/run-salt-master-as-dedicated-salt-user.patch @@ -1,7 +1,7 @@ -From e5db5dcfecab4f1d2e93386ad8c7e579393280d7 Mon Sep 17 00:00:00 2001 +From d1d0fec1dd0fbf6a67c313718975ceb72c10cd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Wed, 20 Jan 2016 11:01:06 +0100 -Subject: [PATCH 02/17] Run salt master as dedicated salt user +Subject: [PATCH] Run salt master as dedicated salt user --- conf/master | 3 ++- @@ -9,7 +9,7 @@ Subject: [PATCH 02/17] Run salt master as dedicated salt user 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/conf/master b/conf/master -index ed09126..ee98e51 100644 +index 3930c9832d..f58eb496bf 100644 --- a/conf/master +++ b/conf/master @@ -25,7 +25,8 @@ @@ -23,7 +23,7 @@ index ed09126..ee98e51 100644 # The port used by the communication interface. The ret (return) port is the # interface used for the file server, authentication, job returns, etc. diff --git a/pkg/salt-common.logrotate b/pkg/salt-common.logrotate -index 3cd0023..8d970c0 100644 +index 3cd002308e..8d970c0a64 100644 --- a/pkg/salt-common.logrotate +++ b/pkg/salt-common.logrotate @@ -1,4 +1,5 @@ @@ -49,5 +49,6 @@ index 3cd0023..8d970c0 100644 missingok rotate 7 -- -2.10.1 +2.11.0 + diff --git a/salt-2016.11.2.tar.gz b/salt-2016.11.2.tar.gz new file mode 100644 index 0000000..c6c975d --- /dev/null +++ b/salt-2016.11.2.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0f1d7ee094ac422a206b6099b84251c49643a2224631d3d532d313baad9ebbb +size 9212850 diff --git a/salt-2016.3.4.tar.gz b/salt-2016.3.4.tar.gz deleted file mode 100644 index c7759a4..0000000 --- a/salt-2016.3.4.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d4c822719d7fb2d79b0103cd9b015d251300890f8aa174e16b73fcfd6eb22f9 -size 8354090 diff --git a/salt.changes b/salt.changes index 64d8afd..c6aa374 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,63 @@ +------------------------------------------------------------------- +Thu Feb 16 14:41:48 UTC 2017 - bmaryniuk@suse.com + +- Update to 2016.11.2 + See https://docs.saltstack.com/en/develop/topics/releases/2016.11.2.html + for full changelog + +- Bugfixes: + * keep fix for migrating salt home directory (bsc#1022562) + * Fix salt-minion update on RHEL (bsc#1022841) + * Prevents 'OSError' exception in case certain job cache path + doesn't exist (bsc#1023535) + +- Added: + * add-a-salt-minion-service-control-file.patch + * add-buildargs-option-to-dockerng.build.patch + * add-ssh-option-to-salt-ssh.patch + * add-yum-plugin.patch + * avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch + * change-travis-configuration-file-to-use-salt-toaster.patch + * docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch + * fix-a-typo-for-master-comments.patch + * fix-error-parsing.patch + * fix-leading-dots-on-sanitized-hostname.patch + * fix-timezone-handling-for-rpm-installtime-bsc-101707.patch + * increasing-timeouts-for-running-integrations-tests.patch + * prevent-crash-on-unknown-to-the-repo-package.patch + * prevents-oserror-exception-in-case-path-doesn-t-exis.patch + * snapper-module-improvements.patch + * support-dryrun-for-dockerng.sls_build.patch + +- Renamed: + * 0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch -> + tserong-suse.com-we-don-t-have-python-systemd-so-not.patch + * 0002-Run-salt-master-as-dedicated-salt-user.patch -> + run-salt-master-as-dedicated-salt-user.patch + * 0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch -> + check-if-byte-strings-are-properly-encoded-in-utf-8.patch + * 0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch -> + do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch + * 0007-Add-zypp-notify-plugin.patch -> + add-zypp-notify-plugin.patch + * 0009-fix-salt-summary-to-count-not-responding-minions-cor.patch -> + fix-salt-summary-to-count-not-responding-minions-cor.patch + * 0010-Run-salt-api-as-user-salt-bsc-990029.patch -> + run-salt-api-as-user-salt-bsc-990029.patch + * 0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch -> + setting-up-os-grains-for-sles-expanded-support-suse-.patch + +- Removed: + * 0005-Use-SHA256-hash-type-by-default.patch + * 0006-Create-salt-proxy-instantiated-service-file.patch + * 0008-snapper-execution-module.patch + * 0011-Fix-snapper_test-for-python26.patch + * 0012-Fix-pkg.upgrade-for-zypper.patch + * 0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch + * 0015-Change-travis-configuration-file-to-use-salt-toaster.patch + * 0016-Fix-pkg.latest_version-when-latest-already-installed.patch + * 0017-Including-resolver-params-for-Zypper-debug-solver.patch + ------------------------------------------------------------------- Wed Nov 2 13:44:18 UTC 2016 - kkaempf@suse.com diff --git a/salt.spec b/salt.spec index 72dcf28..ef8102b 100644 --- a/salt.spec +++ b/salt.spec @@ -1,7 +1,7 @@ # # spec file for package salt # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -37,11 +37,11 @@ %bcond_with builddocs Name: salt -Version: 2016.3.4 +Version: 2016.11.2 Release: 0 Summary: A parallel remote execution system License: Apache-2.0 -Group: System/Monitoring +Group: System/Management Url: http://saltstack.org/ # Git: https://github.com/openSUSE/salt.git Source0: https://pypi.io/packages/source/s/%{name}/%{name}-%{version}.tar.gz @@ -51,56 +51,72 @@ Source3: html.tar.bz2 Source4: update-documentation.sh Source5: travis.yml -# PATCH-FIX-OPENSUSE use-forking-daemon.patch tserong@suse.com -- We don't have python-systemd, so notify can't work # We do not upstream this patch because this is something that we have to fix on our side -Patch1: 0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch -# PATCH-FIX-OPENSUSE use-salt-user-for-master.patch -- Run salt master as dedicated salt user +# PATCH-FIX-OPENSUSE use-forking-daemon.patch tserong@suse.com -- We don't have python-systemd, so notify can't work +Patch1: tserong-suse.com-we-don-t-have-python-systemd-so-not.patch # We do not upstream this patch because this is suse custom configuration -Patch2: 0002-Run-salt-master-as-dedicated-salt-user.patch -# PATCH-FIX-OPENSUSE https://github.com/saltstack/salt/pull/30424 -# We do not upstream this patch because it has been fixed upstream +# PATCH-FIX-OPENSUSE use-salt-user-for-master.patch -- Run salt master as dedicated salt user +Patch2: run-salt-master-as-dedicated-salt-user.patch # (see: https://trello.com/c/wh96lCD4/1528-get-rid-of-0003-check-if-byte-strings-are-properly-encoded-in-utf-8-patch-in-the-salt-package) -Patch3: 0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch -# PATCH-FIX-OPENSUSE prevent rebuilds in OBS +# We do not upstream this patch because it has been fixed upstream +# PATCH-FIX-OPENSUSE https://github.com/saltstack/salt/pull/30424 +Patch3: check-if-byte-strings-are-properly-encoded-in-utf-8.patch # We do not upstream this patch because the issue is on our side -Patch4: 0004-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch -# PATCH-FIX-OPENSUSE - Upstream default hash type is set to MD5, while we require SHA256 (bsc#955373) -# PR https://github.com/saltstack/salt/pull/35341 (15.08.2016 - not merged yet) -Patch5: 0005-Use-SHA256-hash-type-by-default.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/31798 -# PR already merged. This will be gone in the next version -Patch6: 0006-Create-salt-proxy-instantiated-service-file.patch +# PATCH-FIX-OPENSUSE prevent rebuilds in OBS +Patch4: do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch +# We do not upstream this because this is for SUSE only (15.08.2016) # PATCH-FIX-OPENSUSE Generate events from the Salt minion, # if Zypper has been used outside the Salt infrastructure -# We do not upstream this because this is for SUSE only (15.08.2016) -Patch7: 0007-Add-zypp-notify-plugin.patch -# PATCH-FIX_UPSTREAM -Patch8: 0008-snapper-execution-module.patch -# PATCH-FIX_UPSTREAM -Patch9: 0009-fix-salt-summary-to-count-not-responding-minions-cor.patch +Patch5: add-zypp-notify-plugin.patch # PATCH-FIX_OPENSUSE -Patch10: 0010-Run-salt-api-as-user-salt-bsc-990029.patch -# PATCH-FIX_UPSTREAM -Patch11: 0011-Fix-snapper_test-for-python26.patch -# PATCH-FIX_UPSTREAM -Patch12: 0012-Fix-pkg.upgrade-for-zypper.patch -# PATCH-FIX_UPSTREAM -Patch13: 0013-Setting-up-OS-grains-for-SLES-Expanded-Support-SUSE-.patch -# PATCH-FIX_UPSTREAM -Patch14: 0014-acl.delfacl-fix-position-of-X-option-to-setfacl.patch +Patch6: run-salt-api-as-user-salt-bsc-990029.patch # PATCH-FIX_OPENSUSE -Patch15: 0015-Change-travis-configuration-file-to-use-salt-toaster.patch -# PATCH-FIX_UPSTREAM -Patch16: 0016-Fix-pkg.latest_version-when-latest-already-installed.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/37430 -Patch17: 0017-Including-resolver-params-for-Zypper-debug-solver.patch +Patch7: change-travis-configuration-file-to-use-salt-toaster.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/37856 (pending to include in 2016.11) +Patch8: setting-up-os-grains-for-sles-expanded-support-suse-.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/34165 +Patch9: fix-salt-summary-to-count-not-responding-minions-cor.patch +# PATCH-FIX_OPENSUSE +Patch10: avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch +# PATCH-FIX_OPENSUSE +Patch11: add-yum-plugin.patch +# PATCH-FIX_OPENSUSE +Patch12: add-ssh-option-to-salt-ssh.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38639/commits +Patch13: fix-a-typo-for-master-comments.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38806 +Patch14: add-a-salt-minion-service-control-file.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38824 +Patch15: add-buildargs-option-to-dockerng.build.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38885 +Patch16: increasing-timeouts-for-running-integrations-tests.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38957 +Patch17: fix-timezone-handling-for-rpm-installtime-bsc-101707.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38940/commits +Patch18: fix-leading-dots-on-sanitized-hostname.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38991 +Patch19: prevent-crash-on-unknown-to-the-repo-package.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39111 +Patch20: docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39179 +Patch21: fix-error-parsing.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39162 +Patch22: snapper-module-improvements.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39278 +Patch23: support-dryrun-for-dockerng.sls_build.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39400 +Patch24: prevents-oserror-exception-in-case-path-doesn-t-exis.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: logrotate BuildRequires: python BuildRequires: python-devel # requirements/base.txt +%if 0%{?rhel} +BuildRequires: python-jinja2 +%else BuildRequires: python-Jinja2 +%endif BuildRequires: python-futures >= 2.0 BuildRequires: python-markupsafe BuildRequires: python-msgpack-python > 0.3 @@ -160,7 +176,12 @@ Requires: python Requires: python-certifi %endif # requirements/base.txt +%if 0%{?rhel} +Requires: python-jinja2 +Requires: yum +%else Requires: python-Jinja2 +%endif Requires: python-futures >= 2.0 Requires: python-markupsafe Requires: python-msgpack-python > 0.3 @@ -217,6 +238,10 @@ BuildRequires: bash BuildRequires: zsh %endif +%if 0%{?rhel} +BuildRequires: yum +%endif + %description Salt is a distributed remote execution system used to execute commands and query data. It was developed in order to bring the best solutions found in @@ -227,7 +252,7 @@ servers, handle them quickly and through a simple and manageable interface. %package api Summary: The api for Salt a parallel remote execution system -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} Requires: %{name}-master = %{version}-%{release} Requires: python-CherryPy >= 3.2.2 @@ -237,7 +262,7 @@ salt-api is a modular interface on top of Salt that can provide a variety of ent %package cloud Summary: Generic cloud provisioning tool for Saltstack -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} Requires: %{name}-master = %{version}-%{release} Requires: python-apache-libcloud @@ -263,7 +288,7 @@ This contains the documentation of salt, it is an offline version of http://docs %package master Summary: The management component of Saltstack both protocols zmq and raet supported -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} %if 0%{?suse_version} Recommends: python-pygit2 >= 0.20.3 @@ -295,8 +320,9 @@ than serially. %package minion Summary: The client component for Saltstack -Group: System/Monitoring -Requires: %{name} = %{version}-%{release} +Group: System/Management +Requires(pre): %{name} = %{version}-%{release} + %if %{with systemd} %{?systemd_requires} %else @@ -314,7 +340,7 @@ Listens to the salt master and execute the commands. %package raet Summary: Raet Support for Saltstack -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} Requires: python-enum34 Requires: python-ioflo >= 1.1.7 @@ -336,7 +362,7 @@ for trust to be established. %package proxy Summary: Component for salt that enables controlling arbitrary devices -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} %if %{with systemd} %{?systemd_requires} @@ -359,7 +385,7 @@ security reasons, will not. %package syndic Summary: The syndic component for saltstack -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} Requires: %{name}-master = %{version}-%{release} %if %{with systemd} @@ -380,7 +406,7 @@ the management of multiple masters at a time.. %package ssh Summary: Management component for Saltstack with ssh protocol -Group: System/Monitoring +Group: System/Management Requires: %{name} = %{version}-%{release} Requires: %{name}-master = %{version}-%{release} %if 0%{?suse_version} @@ -453,13 +479,14 @@ cp %{S:5} ./.travis.yml %patch2 -p1 %patch3 -p1 %patch4 -p1 -%patch5 -p1 -%patch6 -p1 # This is SUSE-only patch %if 0%{?suse_version} -%patch7 -p1 +%patch5 -p1 %endif + +%patch6 -p1 +%patch7 -p1 %patch8 -p1 %patch9 -p1 %patch10 -p1 @@ -470,6 +497,13 @@ cp %{S:5} ./.travis.yml %patch15 -p1 %patch16 -p1 %patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 %build %{__python} setup.py --salt-transport=both build @@ -507,6 +541,7 @@ install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/queues install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/roots install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/syndics install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/tokens +install -Dd -m 0750 %{buildroot}/var/lib/salt install -Dd -m 0750 %{buildroot}/srv/salt install -Dd -m 0750 %{buildroot}/srv/pillar install -Dd -m 0750 %{buildroot}/srv/spm @@ -532,13 +567,21 @@ install -Dd -m 0750 %{buildroot}%{_prefix}/lib/zypp/plugins/commit %{__install} scripts/zypper/plugins/commit/zyppnotify %{buildroot}%{_prefix}/lib/zypp/plugins/commit/zyppnotify %endif +# Install Yum plugins only on RH machines +%if 0%{?fedora} || 0%{?rhel} +install -Dd %{buildroot}%{_prefix}/share/yum-plugins +install -Dd %{buildroot}/etc/yum/pluginconf.d +%{__install} scripts/yum/plugins/yumnotify.py %{buildroot}%{_prefix}/share/yum-plugins +%{__install} scripts/yum/plugins/yumnotify.conf %{buildroot}/etc/yum/pluginconf.d +%endif + ## install init and systemd scripts %if %{with systemd} install -Dpm 0644 pkg/salt-master.service %{buildroot}%{_unitdir}/salt-master.service -install -Dpm 0644 pkg/salt-minion.service %{buildroot}%{_unitdir}/salt-minion.service +install -Dpm 0644 pkg/suse/salt-minion.service %{buildroot}%{_unitdir}/salt-minion.service install -Dpm 0644 pkg/salt-syndic.service %{buildroot}%{_unitdir}/salt-syndic.service install -Dpm 0644 pkg/salt-api.service %{buildroot}%{_unitdir}/salt-api.service -install -Dpm 0644 pkg/salt-proxy@.service %{buildroot}%{_unitdir}/salt-proxy@.service +install -Dpm 0644 pkg/salt-proxy@.service %{buildroot}%{_unitdir}/salt-proxy@.service ln -s service %{buildroot}%{_sbindir}/rcsalt-master ln -s service %{buildroot}%{_sbindir}/rcsalt-syndic ln -s service %{buildroot}%{_sbindir}/rcsalt-minion @@ -601,8 +644,9 @@ python setup.py test --runtests-opts=-u %endif %pre +getent passwd salt | grep srv\/salt >/dev/null && usermod -d /var/lib/salt salt getent group salt >/dev/null || %{_sbindir}/groupadd -r salt -getent passwd salt >/dev/null || %{_sbindir}/useradd -r -g salt -d /srv/salt -s /bin/false -c "salt-master daemon" salt +getent passwd salt >/dev/null || %{_sbindir}/useradd -r -g salt -d /var/lib/salt -s /bin/false -c "salt-master daemon" salt %post %if %{with systemd} @@ -617,11 +661,6 @@ dbus-uuidgen --ensure %else %if 0%{?suse_version} %stop_on_removal salt-proxy -%else - if [ $1 -eq 0 ] ; then - /sbin/service salt-proxy stop >/dev/null 2>&1 - /sbin/chkconfig --del salt-proxy - fi %endif %endif @@ -879,6 +918,12 @@ fi %{_prefix}/lib/zypp/plugins/commit/zyppnotify %endif +# Install Yum plugins only on RH machines +%if 0%{?fedora} || 0%{?rhel} +%{_prefix}/share/yum-plugins/ +/etc/yum/pluginconf.d/yumnotify.conf +%endif + %if %{with systemd} %{_unitdir}/salt-minion.service %else @@ -922,6 +967,7 @@ fi %dir %attr(0750, salt, salt) %{_sysconfdir}/salt/pki/master/minions_denied/ %dir %attr(0750, salt, salt) %{_sysconfdir}/salt/pki/master/minions_pre/ %dir %attr(0750, salt, salt) %{_sysconfdir}/salt/pki/master/minions_rejected/ +%dir %attr(0755, salt, salt) /var/lib/salt %dir %attr(0755, root, salt) /srv/salt %dir %attr(0755, root, salt) /srv/pillar %dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/ @@ -961,6 +1007,7 @@ fi %if %{with systemd} /usr/lib/tmpfiles.d/salt.conf %endif +%{_mandir}/man1/salt.1.* %if %{with docs} %files doc diff --git a/setting-up-os-grains-for-sles-expanded-support-suse-.patch b/setting-up-os-grains-for-sles-expanded-support-suse-.patch new file mode 100644 index 0000000..99470ef --- /dev/null +++ b/setting-up-os-grains-for-sles-expanded-support-suse-.patch @@ -0,0 +1,28 @@ +From 5a07f204d45b2b86d8bc0279527723e030cc4e21 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= + +Date: Fri, 30 Sep 2016 13:06:52 +0100 +Subject: [PATCH] Setting up OS grains for SLES Expanded Support (SUSE's + Red Hat compatible platform) + +core.py: quote style fixed +--- + salt/grains/core.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/salt/grains/core.py b/salt/grains/core.py +index 6a42cc734f..fce35cb313 100644 +--- a/salt/grains/core.py ++++ b/salt/grains/core.py +@@ -1049,6 +1049,7 @@ _OS_NAME_MAP = { + 'sles': 'SUSE', + 'slesexpand': 'RES', + 'void': 'Void', ++ 'slesexpand': 'RES', + 'linuxmint': 'Mint', + } + +-- +2.11.0 + + diff --git a/snapper-module-improvements.patch b/snapper-module-improvements.patch new file mode 100644 index 0000000..f0f6964 --- /dev/null +++ b/snapper-module-improvements.patch @@ -0,0 +1,275 @@ +From 678de7117211fc359c9aa7e29f6c2fecf0944b08 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= + +Date: Fri, 27 Jan 2017 17:07:25 +0000 +Subject: [PATCH] Snapper module improvements + +* Snapper: Adding support for deleting snapshots +* Snapper: Adding support for snapshot metadata modification +* Snapper: Adding support for creating configurations +* Adds 'snapper.delete_snapshots' unit tests +* Adds 'snapper.modify_snapshots' unit tests +* Adds 'snapper.create_config' unit tests +* Removing extra spaces +* pylint fixes +--- + salt/modules/snapper.py | 159 +++++++++++++++++++++++++++++++++++-- + tests/unit/modules/snapper_test.py | 50 ++++++++++++ + 2 files changed, 201 insertions(+), 8 deletions(-) + +diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py +index 318ce9b99d..d5f1181743 100644 +--- a/salt/modules/snapper.py ++++ b/salt/modules/snapper.py +@@ -290,6 +290,60 @@ def get_config(name='root'): + ) + + ++def create_config(name=None, ++ subvolume=None, ++ fstype=None, ++ template=None, ++ extra_opts=None): ++ ''' ++ Creates a new Snapper configuration ++ ++ name ++ Name of the new Snapper configuration. ++ subvolume ++ Path to the related subvolume. ++ fstype ++ Filesystem type of the subvolume. ++ template ++ Configuration template to use. (Default: default) ++ extra_opts ++ Extra Snapper configuration opts dictionary. It will override the values provided ++ by the given template (if any). ++ ++ CLI example: ++ ++ .. code-block:: bash ++ ++ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs ++ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs template="default" ++ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs extra_opts='{"NUMBER_CLEANUP": False}' ++ ''' ++ def raise_arg_error(argname): ++ raise CommandExecutionError( ++ 'You must provide a "{0}" for the new configuration'.format(argname) ++ ) ++ ++ if not name: ++ raise_arg_error("name") ++ if not subvolume: ++ raise_arg_error("subvolume") ++ if not fstype: ++ raise_arg_error("fstype") ++ if not template: ++ template = "" ++ ++ try: ++ snapper.CreateConfig(name, subvolume, fstype, template) ++ if extra_opts: ++ set_config(name, **extra_opts) ++ return get_config(name) ++ except dbus.DBusException as exc: ++ raise CommandExecutionError( ++ 'Error encountered while creating the new configuration: {0}' ++ .format(_dbus_exception_to_reason(exc, locals())) ++ ) ++ ++ + def create_snapshot(config='root', snapshot_type='single', pre_number=None, + description=None, cleanup_algorithm='number', userdata=None, + **kwargs): +@@ -309,14 +363,14 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, + cleanup_algorithm + Set the cleanup algorithm for the snapshot. + +- number +- Deletes old snapshots when a certain number of snapshots +- is reached. +- timeline +- Deletes old snapshots but keeps a number of hourly, +- daily, weekly, monthly and yearly snapshots. +- empty-pre-post +- Deletes pre/post snapshot pairs with empty diffs. ++ number ++ Deletes old snapshots when a certain number of snapshots ++ is reached. ++ timeline ++ Deletes old snapshots but keeps a number of hourly, ++ daily, weekly, monthly and yearly snapshots. ++ empty-pre-post ++ Deletes pre/post snapshot pairs with empty diffs. + userdata + Set userdata for the snapshot (key-value pairs). + +@@ -364,6 +418,95 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, + return new_nr + + ++def delete_snapshot(snapshots_ids=None, config="root"): ++ ''' ++ Deletes an snapshot ++ ++ config ++ Configuration name. (Default: root) ++ ++ snapshots_ids ++ List of the snapshots IDs to be deleted. ++ ++ CLI example: ++ ++ .. code-block:: bash ++ ++ salt '*' snapper.delete_snapshot 54 ++ salt '*' snapper.delete_snapshot config=root 54 ++ salt '*' snapper.delete_snapshot config=root snapshots_ids=[54,55,56] ++ ''' ++ if not snapshots_ids: ++ raise CommandExecutionError('Error: No snapshot ID has been provided') ++ try: ++ current_snapshots_ids = [x['id'] for x in list_snapshots(config)] ++ if not isinstance(snapshots_ids, list): ++ snapshots_ids = [snapshots_ids] ++ if not set(snapshots_ids).issubset(set(current_snapshots_ids)): ++ raise CommandExecutionError( ++ "Error: Snapshots '{0}' not found".format(", ".join( ++ [str(x) for x in set(snapshots_ids).difference( ++ set(current_snapshots_ids))])) ++ ) ++ snapper.DeleteSnapshots(config, snapshots_ids) ++ return {config: {"ids": snapshots_ids, "status": "deleted"}} ++ except dbus.DBusException as exc: ++ raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) ++ ++ ++def modify_snapshot(snapshot_id=None, ++ description=None, ++ userdata=None, ++ cleanup=None, ++ config="root"): ++ ''' ++ Modify attributes of an existing snapshot. ++ ++ config ++ Configuration name. (Default: root) ++ ++ snapshot_id ++ ID of the snapshot to be modified. ++ ++ cleanup ++ Change the cleanup method of the snapshot. (str) ++ ++ description ++ Change the description of the snapshot. (str) ++ ++ userdata ++ Change the userdata dictionary of the snapshot. (dict) ++ ++ CLI example: ++ ++ .. code-block:: bash ++ ++ salt '*' snapper.modify_snapshot 54 description="my snapshot description" ++ salt '*' snapper.modify_snapshot 54 description="my snapshot description" ++ salt '*' snapper.modify_snapshot 54 userdata='{"foo": "bar"}' ++ salt '*' snapper.modify_snapshot snapshot_id=54 cleanup="number" ++ ''' ++ if not snapshot_id: ++ raise CommandExecutionError('Error: No snapshot ID has been provided') ++ ++ snapshot = get_snapshot(config=config, number=snapshot_id) ++ try: ++ # Updating only the explicitely provided attributes by the user ++ updated_opts = { ++ 'description': description if description is not None else snapshot['description'], ++ 'cleanup': cleanup if cleanup is not None else snapshot['cleanup'], ++ 'userdata': userdata if userdata is not None else snapshot['userdata'], ++ } ++ snapper.SetSnapshot(config, ++ snapshot_id, ++ updated_opts['description'], ++ updated_opts['cleanup'], ++ updated_opts['userdata']) ++ return get_snapshot(config=config, number=snapshot_id) ++ except dbus.DBusException as exc: ++ raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) ++ ++ + def _get_num_interval(config, num_pre, num_post): + ''' + Returns numerical interval based on optionals num_pre, num_post values +diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py +index ca985cfd05..a5d9b7686e 100644 +--- a/tests/unit/modules/snapper_test.py ++++ b/tests/unit/modules/snapper_test.py +@@ -202,6 +202,26 @@ class SnapperTestCase(TestCase): + self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"]) + self.assertEqual(snapper.status_to_string(256), ["ACL info changed"]) + ++ @patch('salt.modules.snapper.snapper.CreateConfig', MagicMock()) ++ @patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])) ++ def test_create_config(self): ++ opts = { ++ 'name': 'testconfig', ++ 'subvolume': '/foo/bar/', ++ 'fstype': 'btrfs', ++ 'template': 'mytemplate', ++ 'extra_opts': {"NUMBER_CLEANUP": False}, ++ } ++ with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: ++ self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) ++ set_config_mock.assert_called_with("testconfig", **opts['extra_opts']) ++ ++ with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: ++ del opts['extra_opts'] ++ self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) ++ assert not set_config_mock.called ++ self.assertRaises(CommandExecutionError, snapper.create_config) ++ + @patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234)) + @patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234)) + @patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234)) +@@ -216,6 +236,36 @@ class SnapperTestCase(TestCase): + } + self.assertEqual(snapper.create_snapshot(**opts), 1234) + ++ @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) ++ @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) ++ def test_delete_snapshot_id_success(self): ++ self.assertEqual(snapper.delete_snapshot(snapshots_ids=43), {"root": {"ids": [43], "status": "deleted"}}) ++ self.assertEqual(snapper.delete_snapshot(snapshots_ids=[42, 43]), {"root": {"ids": [42, 43], "status": "deleted"}}) ++ ++ @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) ++ @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) ++ def test_delete_snapshot_id_fail(self): ++ self.assertRaises(CommandExecutionError, snapper.delete_snapshot) ++ self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1) ++ self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]) ++ ++ @patch('salt.modules.snapper.snapper.SetSnapshot', MagicMock()) ++ def test_modify_snapshot(self): ++ _ret = { ++ 'userdata': {'userdata2': 'uservalue2'}, ++ 'description': 'UPDATED DESCRIPTION', 'timestamp': 1457006571, ++ 'cleanup': 'number', 'user': 'root', 'type': 'pre', 'id': 42 ++ } ++ _opts = { ++ 'config': 'root', ++ 'snapshot_id': 42, ++ 'cleanup': 'number', ++ 'description': 'UPDATED DESCRIPTION', ++ 'userdata': {'userdata2': 'uservalue2'}, ++ } ++ with patch('salt.modules.snapper.get_snapshot', MagicMock(side_effect=[DBUS_RET['ListSnapshots'][0], _ret])): ++ self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret) ++ + @patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})) + def test__get_num_interval(self): + self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access +-- +2.11.0 + + diff --git a/support-dryrun-for-dockerng.sls_build.patch b/support-dryrun-for-dockerng.sls_build.patch new file mode 100644 index 0000000..9de2a0e --- /dev/null +++ b/support-dryrun-for-dockerng.sls_build.patch @@ -0,0 +1,187 @@ +From ec727bbd931c8536b4f3cb1bf3f599432c063b3e Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Thu, 9 Feb 2017 12:44:43 +0100 +Subject: [PATCH] support dryrun for dockerng.sls_build + +provide the possibility to put extra modules into the thin + +added unit test for dockerng.sls_build dryrun option +--- + salt/modules/dockerng.py | 29 +++++++++++-- + tests/unit/modules/dockerng_test.py | 82 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 108 insertions(+), 3 deletions(-) + +diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py +index cbf2a6b397..bd0a208cac 100644 +--- a/salt/modules/dockerng.py ++++ b/salt/modules/dockerng.py +@@ -5757,7 +5757,9 @@ def call(name, function, *args, **kwargs): + raise CommandExecutionError('Missing function parameter') + + # move salt into the container +- thin_path = salt.utils.thin.gen_thin(__opts__['cachedir']) ++ thin_path = salt.utils.thin.gen_thin(__opts__['cachedir'], ++ extra_mods=__salt__['config.option']("thin_extra_mods", ''), ++ so_mods=__salt__['config.option']("thin_so_mods", '')) + with io.open(thin_path, 'rb') as file: + _client_wrapper('put_archive', name, thin_dest_path, file) + try: +@@ -5867,7 +5869,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): + + + def sls_build(name, base='opensuse/python', mods=None, saltenv='base', +- **kwargs): ++ dryrun=False, **kwargs): + ''' + Build a docker image using the specified sls modules and base image. + +@@ -5875,6 +5877,24 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', + can build a docker image inside myminion that results of applying those + states by doing: + ++ base ++ the base image ++ ++ mods ++ the state modules to execute during build ++ ++ saltenv ++ the salt environment to use ++ ++ dryrun: False ++ when set to True the container will not be commited at the end of ++ the build. The dryrun succeed also when the state contains errors. ++ ++ **RETURN DATA** ++ ++ A dictionary with the ID of the new container. In case of a dryrun, ++ the state result is returned and the container gets removed. ++ + CLI Example: + + .. code-block:: bash +@@ -5907,9 +5927,12 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', + # Now execute the state into the container + ret = __salt__['dockerng.sls'](id_, mods, saltenv, **kwargs) + # fail if the state was not successful +- if not salt.utils.check_state_result(ret): ++ if not dryrun and not salt.utils.check_state_result(ret): + raise CommandExecutionError(ret) + finally: + __salt__['dockerng.stop'](id_) + ++ if dryrun: ++ __salt__['dockerng.rm'](id_) ++ return ret + return __salt__['dockerng.commit'](id_, name) +diff --git a/tests/unit/modules/dockerng_test.py b/tests/unit/modules/dockerng_test.py +index ec4901e275..546635577a 100644 +--- a/tests/unit/modules/dockerng_test.py ++++ b/tests/unit/modules/dockerng_test.py +@@ -680,6 +680,84 @@ class DockerngTestCase(TestCase): + self.assertEqual( + {'Id': 'ID2', 'Image': 'foo', 'Time_Elapsed': 42}, ret) + ++ def test_sls_build_dryrun(self, *args): ++ ''' ++ test build sls image in dryrun mode. ++ ''' ++ docker_start_mock = MagicMock( ++ return_value={}) ++ docker_create_mock = MagicMock( ++ return_value={'Id': 'ID', 'Name': 'NAME'}) ++ docker_stop_mock = MagicMock( ++ return_value={'state': {'old': 'running', 'new': 'stopped'}, ++ 'result': True}) ++ docker_rm_mock = MagicMock( ++ return_value={}) ++ ++ docker_sls_mock = MagicMock( ++ return_value={ ++ "file_|-/etc/test.sh_|-/etc/test.sh_|-managed": { ++ "comment": "File /etc/test.sh is in the correct state", ++ "name": "/etc/test.sh", ++ "start_time": "07:04:26.834792", ++ "result": True, ++ "duration": 13.492, ++ "__run_num__": 0, ++ "changes": {} ++ }, ++ "test_|-always-passes_|-foo_|-succeed_without_changes": { ++ "comment": "Success!", ++ "name": "foo", ++ "start_time": "07:04:26.848915", ++ "result": True, ++ "duration": 0.363, ++ "__run_num__": 1, ++ "changes": {} ++ } ++ }) ++ ++ ret = None ++ with patch.dict(dockerng_mod.__salt__, { ++ 'dockerng.start': docker_start_mock, ++ 'dockerng.create': docker_create_mock, ++ 'dockerng.stop': docker_stop_mock, ++ 'dockerng.rm': docker_rm_mock, ++ 'dockerng.sls': docker_sls_mock}): ++ ret = dockerng_mod.sls_build( ++ 'foo', ++ mods='foo', ++ dryrun=True ++ ) ++ docker_create_mock.assert_called_once_with( ++ cmd='sleep infinity', ++ image='opensuse/python', interactive=True, name='foo', tty=True) ++ docker_start_mock.assert_called_once_with('ID') ++ docker_sls_mock.assert_called_once_with('ID', 'foo', 'base') ++ docker_stop_mock.assert_called_once_with('ID') ++ docker_rm_mock.assert_called_once_with('ID') ++ self.assertEqual( ++ { ++ "file_|-/etc/test.sh_|-/etc/test.sh_|-managed": { ++ "comment": "File /etc/test.sh is in the correct state", ++ "name": "/etc/test.sh", ++ "start_time": "07:04:26.834792", ++ "result": True, ++ "duration": 13.492, ++ "__run_num__": 0, ++ "changes": {} ++ }, ++ "test_|-always-passes_|-foo_|-succeed_without_changes": { ++ "comment": "Success!", ++ "name": "foo", ++ "start_time": "07:04:26.848915", ++ "result": True, ++ "duration": 0.363, ++ "__run_num__": 1, ++ "changes": {} ++ } ++ }, ++ ret) ++ + def test_call_success(self): + ''' + test module calling inside containers +@@ -694,6 +772,9 @@ class DockerngTestCase(TestCase): + return_value={ + 'retcode': 0 + }) ++ docker_config_mock = MagicMock( ++ return_value='' ++ ) + client = Mock() + client.put_archive = Mock() + +@@ -704,6 +785,7 @@ class DockerngTestCase(TestCase): + dockerng_mod.__salt__, { + 'dockerng.run_all': docker_run_all_mock, + 'dockerng.copy_to': docker_copy_to_mock, ++ 'config.option': docker_config_mock + }), + patch.dict( + dockerng_mod.__context__, { +-- +2.11.0 + + diff --git a/0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch b/tserong-suse.com-we-don-t-have-python-systemd-so-not.patch similarity index 73% rename from 0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch rename to tserong-suse.com-we-don-t-have-python-systemd-so-not.patch index 1512813..fc19976 100644 --- a/0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch +++ b/tserong-suse.com-we-don-t-have-python-systemd-so-not.patch @@ -1,7 +1,7 @@ -From 945e87477d66ada90e914e95acd5ff529477012a Mon Sep 17 00:00:00 2001 +From a9f1be35b0c158fcdd460dcc8c501fe039d97258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Wed, 20 Jan 2016 11:00:15 +0100 -Subject: [PATCH 01/17] tserong@suse.com -- We don't have python-systemd, so +Subject: [PATCH] tserong@suse.com -- We don't have python-systemd, so notify can't work --- @@ -9,7 +9,7 @@ Subject: [PATCH 01/17] tserong@suse.com -- We don't have python-systemd, so 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/salt-master.service b/pkg/salt-master.service -index 1f4650f..59be503 100644 +index 1f4650f872..59be50301a 100644 --- a/pkg/salt-master.service +++ b/pkg/salt-master.service @@ -4,8 +4,7 @@ After=network.target @@ -23,5 +23,6 @@ index 1f4650f..59be503 100644 [Install] -- -2.10.1 +2.11.0 + From 02cd6e00870e9e7d2bc341526f384472aa775fb94ea3c35dde1ded136def3d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Fri, 17 Feb 2017 09:49:05 +0000 Subject: [PATCH 2/3] Accepting request 458645 from home:bmaryniuk:branches:systemsmanagement:saltstack Update source tarball from the official place OBS-URL: https://build.opensuse.org/request/show/458645 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=86 --- salt-2016.11.2.tar.gz | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt-2016.11.2.tar.gz b/salt-2016.11.2.tar.gz index c6c975d..2182004 100644 --- a/salt-2016.11.2.tar.gz +++ b/salt-2016.11.2.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0f1d7ee094ac422a206b6099b84251c49643a2224631d3d532d313baad9ebbb -size 9212850 +oid sha256:f5c3d3cf4293d5b80a93790c76dec61421991c9c54222abd7327b3437ad13a43 +size 9114042 From 1033bb543dbf54b23ad80273e8de990d6536f85a4649483082d7e8b63d35b05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Fri, 3 Mar 2017 14:58:24 +0000 Subject: [PATCH 3/3] Accepting request 473298 from home:bmaryniuk:branches:systemsmanagement:saltstack Update to 2016.11.3 version, pkg deps fix OBS-URL: https://build.opensuse.org/request/show/473298 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=87 --- add-buildargs-option-to-dockerng.build.patch | 48 --- ...ld.patch => add-options-for-dockerng.patch | 85 +++++- add-ssh-option-to-salt-ssh.patch | 30 +- ...-require-minimal-dockerpy-1.6.0-and-.patch | 48 --- fix-a-typo-for-master-comments.patch | 32 -- fix-case-in-os_family-for-suse.patch | 26 ++ fix-error-parsing.patch | 26 -- fix-leading-dots-on-sanitized-hostname.patch | 94 ------ ...n-in-file.get_managed-add-unit-tests.patch | 196 +++++++++++++ ...dling-for-rpm-installtime-bsc-101707.patch | 34 --- ...eouts-for-running-integrations-tests.patch | 170 ----------- ...crash-on-unknown-to-the-repo-package.patch | 26 -- ...-exception-in-case-path-doesn-t-exis.patch | 28 -- salt-2016.11.2.tar.gz | 3 - salt-2016.11.3.tar.gz | 3 + salt.changes | 31 ++ salt.spec | 62 ++-- snapper-module-improvements.patch | 275 ------------------ ...le-arguments-if-they-contain-hidden-.patch | 91 ++++++ 19 files changed, 457 insertions(+), 851 deletions(-) delete mode 100644 add-buildargs-option-to-dockerng.build.patch rename support-dryrun-for-dockerng.sls_build.patch => add-options-for-dockerng.patch (71%) delete mode 100644 docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch delete mode 100644 fix-a-typo-for-master-comments.patch create mode 100644 fix-case-in-os_family-for-suse.patch delete mode 100644 fix-error-parsing.patch delete mode 100644 fix-leading-dots-on-sanitized-hostname.patch create mode 100644 fix-regression-in-file.get_managed-add-unit-tests.patch delete mode 100644 fix-timezone-handling-for-rpm-installtime-bsc-101707.patch delete mode 100644 increasing-timeouts-for-running-integrations-tests.patch delete mode 100644 prevent-crash-on-unknown-to-the-repo-package.patch delete mode 100644 prevents-oserror-exception-in-case-path-doesn-t-exis.patch delete mode 100644 salt-2016.11.2.tar.gz create mode 100644 salt-2016.11.3.tar.gz delete mode 100644 snapper-module-improvements.patch create mode 100644 translate-variable-arguments-if-they-contain-hidden-.patch diff --git a/add-buildargs-option-to-dockerng.build.patch b/add-buildargs-option-to-dockerng.build.patch deleted file mode 100644 index 8b76c26..0000000 --- a/add-buildargs-option-to-dockerng.build.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 409e1835e0f26295212d1d148d1e7b42cf7dc6b0 Mon Sep 17 00:00:00 2001 -From: Michael Calmer -Date: Thu, 19 Jan 2017 15:39:10 +0100 -Subject: [PATCH] add buildargs option to dockerng.build - ---- - salt/modules/dockerng.py | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py -index 6c31c18289..87000e3846 100644 ---- a/salt/modules/dockerng.py -+++ b/salt/modules/dockerng.py -@@ -3553,7 +3553,8 @@ def build(path=None, - rm=True, - api_response=False, - fileobj=None, -- dockerfile=None): -+ dockerfile=None, -+ buildargs=None): - ''' - Builds a docker image from a Dockerfile or a URL - -@@ -3587,6 +3588,10 @@ def build(path=None, - - .. versionadded:: develop - -+ buildargs -+ A dictionary of build arguments provided to the docker build process. -+ -+ - **RETURN DATA** - - A dictionary containing one or more of the following keys: -@@ -3633,7 +3638,8 @@ def build(path=None, - fileobj=fileobj, - rm=rm, - nocache=not cache, -- dockerfile=dockerfile) -+ dockerfile=dockerfile, -+ buildargs=buildargs) - ret = {'Time_Elapsed': time.time() - time_started} - _clear_context() - --- -2.11.0 - - diff --git a/support-dryrun-for-dockerng.sls_build.patch b/add-options-for-dockerng.patch similarity index 71% rename from support-dryrun-for-dockerng.sls_build.patch rename to add-options-for-dockerng.patch index 9de2a0e..02bd0c8 100644 --- a/support-dryrun-for-dockerng.sls_build.patch +++ b/add-options-for-dockerng.patch @@ -1,21 +1,86 @@ -From ec727bbd931c8536b4f3cb1bf3f599432c063b3e Mon Sep 17 00:00:00 2001 +From 95d547d3e6c98f927eaad77346b84b2ccef95627 Mon Sep 17 00:00:00 2001 From: Michael Calmer -Date: Thu, 9 Feb 2017 12:44:43 +0100 -Subject: [PATCH] support dryrun for dockerng.sls_build +Date: Thu, 19 Jan 2017 15:39:10 +0100 +Subject: [PATCH] add options for dockerng + +* add buildargs option to dockerng.build +* docker buildargs require minimal dockerpy 1.6.0 and docker version 1.9.0 +* update version in the docs +* support dryrun for dockerng.sls_build provide the possibility to put extra modules into the thin added unit test for dockerng.sls_build dryrun option --- - salt/modules/dockerng.py | 29 +++++++++++-- + salt/modules/dockerng.py | 51 ++++++++++++++++++----- tests/unit/modules/dockerng_test.py | 82 +++++++++++++++++++++++++++++++++++++ - 2 files changed, 108 insertions(+), 3 deletions(-) + 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py -index cbf2a6b397..bd0a208cac 100644 +index 29af703ffa..c199fede1e 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py -@@ -5757,7 +5757,9 @@ def call(name, function, *args, **kwargs): +@@ -28,13 +28,13 @@ to replace references to ``dockerng`` with ``docker``. + Installation Prerequisites + -------------------------- + +-This execution module requires at least version 1.4.0 of both docker-py_ and +-Docker_. docker-py can easily be installed using :py:func:`pip.install +-`: ++This execution module requires at least version 1.6.0 of docker-py_ and ++version 1.9.0 of Docker_. docker-py can easily be installed using ++:py:func:`pip.install `: + + .. code-block:: bash + +- salt myminion pip.install docker-py>=1.4.0 ++ salt myminion pip.install docker-py>=1.6.0 + + .. _docker-py: https://pypi.python.org/pypi/docker-py + .. _Docker: https://www.docker.com/ +@@ -332,8 +332,8 @@ __func_alias__ = { + } + + # Minimum supported versions +-MIN_DOCKER = (1, 6, 0) +-MIN_DOCKER_PY = (1, 4, 0) ++MIN_DOCKER = (1, 9, 0) ++MIN_DOCKER_PY = (1, 6, 0) + + VERSION_RE = r'([\d.]+)' + +@@ -3557,7 +3557,8 @@ def build(path=None, + rm=True, + api_response=False, + fileobj=None, +- dockerfile=None): ++ dockerfile=None, ++ buildargs=None): + ''' + Builds a docker image from a Dockerfile or a URL + +@@ -3591,6 +3592,10 @@ def build(path=None, + + .. versionadded:: develop + ++ buildargs ++ A dictionary of build arguments provided to the docker build process. ++ ++ + **RETURN DATA** + + A dictionary containing one or more of the following keys: +@@ -3637,7 +3642,8 @@ def build(path=None, + fileobj=fileobj, + rm=rm, + nocache=not cache, +- dockerfile=dockerfile) ++ dockerfile=dockerfile, ++ buildargs=buildargs) + ret = {'Time_Elapsed': time.time() - time_started} + _clear_context() + +@@ -5755,7 +5761,9 @@ def call(name, function, *args, **kwargs): raise CommandExecutionError('Missing function parameter') # move salt into the container @@ -26,7 +91,7 @@ index cbf2a6b397..bd0a208cac 100644 with io.open(thin_path, 'rb') as file: _client_wrapper('put_archive', name, thin_dest_path, file) try: -@@ -5867,7 +5869,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): +@@ -5865,7 +5873,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): def sls_build(name, base='opensuse/python', mods=None, saltenv='base', @@ -35,7 +100,7 @@ index cbf2a6b397..bd0a208cac 100644 ''' Build a docker image using the specified sls modules and base image. -@@ -5875,6 +5877,24 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', +@@ -5873,6 +5881,24 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', can build a docker image inside myminion that results of applying those states by doing: @@ -60,7 +125,7 @@ index cbf2a6b397..bd0a208cac 100644 CLI Example: .. code-block:: bash -@@ -5907,9 +5927,12 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', +@@ -5905,9 +5931,12 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', # Now execute the state into the container ret = __salt__['dockerng.sls'](id_, mods, saltenv, **kwargs) # fail if the state was not successful diff --git a/add-ssh-option-to-salt-ssh.patch b/add-ssh-option-to-salt-ssh.patch index ec495d7..5b9e3fb 100644 --- a/add-ssh-option-to-salt-ssh.patch +++ b/add-ssh-option-to-salt-ssh.patch @@ -1,4 +1,4 @@ -From 949591bc0a4c0e768c08a2f61c040a1c4f0190ed Mon Sep 17 00:00:00 2001 +From c4c6610bf7314cc4c6ecf656bef341e2d1ca1587 Mon Sep 17 00:00:00 2001 From: Matei Albu Date: Mon, 19 Dec 2016 16:54:52 +0100 Subject: [PATCH] Add --ssh-option to salt-ssh @@ -16,7 +16,7 @@ Fix salt-ssh err when -ssh-option is missing 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py -index f0b7a282c0..9ed6be2b75 100644 +index 23ec948fe0..bbef9d8de1 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -295,6 +295,9 @@ class SSH(object): @@ -29,7 +29,7 @@ index f0b7a282c0..9ed6be2b75 100644 } if self.opts.get('rand_thin_dir'): self.defaults['thin_dir'] = os.path.join( -@@ -690,6 +693,7 @@ class Single(object): +@@ -693,6 +696,7 @@ class Single(object): identities_only=False, sudo_user=None, remote_port_forwards=None, @@ -37,7 +37,7 @@ index f0b7a282c0..9ed6be2b75 100644 **kwargs): # Get mine setting and mine_functions if defined in kwargs (from roster) self.mine = mine -@@ -746,7 +750,8 @@ class Single(object): +@@ -749,7 +753,8 @@ class Single(object): 'mods': self.mods, 'identities_only': identities_only, 'sudo_user': sudo_user, @@ -48,7 +48,7 @@ index f0b7a282c0..9ed6be2b75 100644 self.minion_opts = { 'grains_cache': True, diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py -index 722ec07633..2884a52c1d 100644 +index f78cb623e1..613660fe34 100644 --- a/salt/client/ssh/shell.py +++ b/salt/client/ssh/shell.py @@ -64,7 +64,8 @@ class Shell(object): @@ -59,9 +59,9 @@ index 722ec07633..2884a52c1d 100644 + remote_port_forwards=None, + ssh_options=None): self.opts = opts - self.host = host - self.user = user -@@ -77,6 +78,7 @@ class Shell(object): + # ssh , but scp [ -Date: Wed, 1 Feb 2017 16:57:13 +0100 -Subject: [PATCH] docker buildargs require minimal dockerpy 1.6.0 and - docker version 1.9.0 - -update version in the docs ---- - salt/modules/dockerng.py | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py -index 87000e3846..f29108f1ba 100644 ---- a/salt/modules/dockerng.py -+++ b/salt/modules/dockerng.py -@@ -28,13 +28,13 @@ to replace references to ``dockerng`` with ``docker``. - Installation Prerequisites - -------------------------- - --This execution module requires at least version 1.4.0 of both docker-py_ and --Docker_. docker-py can easily be installed using :py:func:`pip.install --`: -+This execution module requires at least version 1.6.0 of docker-py_ and -+version 1.9.0 of Docker_. docker-py can easily be installed using -+:py:func:`pip.install `: - - .. code-block:: bash - -- salt myminion pip.install docker-py>=1.4.0 -+ salt myminion pip.install docker-py>=1.6.0 - - .. _docker-py: https://pypi.python.org/pypi/docker-py - .. _Docker: https://www.docker.com/ -@@ -332,8 +332,8 @@ __func_alias__ = { - } - - # Minimum supported versions --MIN_DOCKER = (1, 6, 0) --MIN_DOCKER_PY = (1, 4, 0) -+MIN_DOCKER = (1, 9, 0) -+MIN_DOCKER_PY = (1, 6, 0) - - VERSION_RE = r'([\d.]+)' - --- -2.11.0 - - diff --git a/fix-a-typo-for-master-comments.patch b/fix-a-typo-for-master-comments.patch deleted file mode 100644 index 9789cc2..0000000 --- a/fix-a-typo-for-master-comments.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 64ddef78a040dde7e547518331dc9e8a98ceb42f Mon Sep 17 00:00:00 2001 -From: Bo Maryniuk -Date: Thu, 5 Jan 2017 19:09:17 +0100 -Subject: [PATCH] Fix a typo for master comments - -* Disable custom rosters in API -* Pick up a specified roster file from the configured locations -* Explain what it is about and how to configure that -* Add docstring -* Add explanation comment -* Lintfix ---- - conf/master | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/conf/master b/conf/master -index f58eb496bf..3cfdae0a47 100644 ---- a/conf/master -+++ b/conf/master -@@ -405,7 +405,7 @@ syndic_user: salt - # Pass in an alternative location for the salt-ssh roster file - #roster_file: /etc/salt/roster - --# Define a location for roster files so they can be chosen when using Salt API. -+# Define a locations for roster files so they can be chosen when using Salt API. - # An administrator can place roster files into these locations. Then when - # calling Salt API, parameter 'roster_file' should contain a relative path to - # these locations. That is, "roster_file=/foo/roster" will be resolved as --- -2.11.0 - - diff --git a/fix-case-in-os_family-for-suse.patch b/fix-case-in-os_family-for-suse.patch new file mode 100644 index 0000000..bced534 --- /dev/null +++ b/fix-case-in-os_family-for-suse.patch @@ -0,0 +1,26 @@ +From 036be5f7300bbf6c5ef3967b5cc935fd678cd1e1 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Thu, 23 Feb 2017 12:01:05 +0100 +Subject: [PATCH] fix case in os_family for Suse + +--- + salt/modules/service.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/salt/modules/service.py b/salt/modules/service.py +index bb7133ee99..49186e4c9d 100644 +--- a/salt/modules/service.py ++++ b/salt/modules/service.py +@@ -53,7 +53,7 @@ def __virtual__(): + if __grains__['kernel'] != 'Linux': + return (False, 'Non Linux OSes are not supported') + # SUSE >=12.0 uses systemd +- if __grains__.get('os_family', '') == 'SUSE': ++ if __grains__.get('os_family', '') == 'Suse': + try: + # osrelease might be in decimal format (e.g. "12.1"), or for + # SLES might include service pack (e.g. "11 SP3"), so split on +-- +2.11.0 + + diff --git a/fix-error-parsing.patch b/fix-error-parsing.patch deleted file mode 100644 index 1bb6bdf..0000000 --- a/fix-error-parsing.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 7c459f38d43ef31ededce703fbfb432af1fdcee6 Mon Sep 17 00:00:00 2001 -From: Michael Calmer -Date: Sat, 4 Feb 2017 16:05:45 +0100 -Subject: [PATCH] fix error parsing - ---- - salt/modules/dockerng.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py -index f29108f1ba..cbf2a6b397 100644 ---- a/salt/modules/dockerng.py -+++ b/salt/modules/dockerng.py -@@ -1204,7 +1204,7 @@ def _error_detail(data, item): - ''' - err = item['errorDetail'] - if 'code' in err: -- msg = '{1}: {2}'.format( -+ msg = '{0}: {1}'.format( - item['errorDetail']['code'], - item['errorDetail']['message'], - ) --- -2.11.0 - - diff --git a/fix-leading-dots-on-sanitized-hostname.patch b/fix-leading-dots-on-sanitized-hostname.patch deleted file mode 100644 index af77429..0000000 --- a/fix-leading-dots-on-sanitized-hostname.patch +++ /dev/null @@ -1,94 +0,0 @@ -From dadd20bd0db0289ac4ad9f322f03de686b24c5a7 Mon Sep 17 00:00:00 2001 -From: Bo Maryniuk -Date: Wed, 25 Jan 2017 15:42:08 +0100 -Subject: [PATCH] Fix leading dots on sanitized hostname - -Add unit test - -Fix typo ---- - salt/utils/sanitizers.py | 2 +- - tests/unit/utils/sanitizers_test.py | 57 +++++++++++++++++++++++++++++++++++++ - 2 files changed, 58 insertions(+), 1 deletion(-) - create mode 100644 tests/unit/utils/sanitizers_test.py - -diff --git a/salt/utils/sanitizers.py b/salt/utils/sanitizers.py -index d05d39955d..83f621151a 100644 ---- a/salt/utils/sanitizers.py -+++ b/salt/utils/sanitizers.py -@@ -56,7 +56,7 @@ class InputSanitizer(object): - :param value: - :return: - ''' -- return re.sub(r'[^a-zA-Z0-9.-]', '', InputSanitizer.trim(value)) -+ return re.sub(r'[^a-zA-Z0-9.-]', '', InputSanitizer.trim(value)).strip('.') - - id = hostname - -diff --git a/tests/unit/utils/sanitizers_test.py b/tests/unit/utils/sanitizers_test.py -new file mode 100644 -index 0000000000..e9c333149c ---- /dev/null -+++ b/tests/unit/utils/sanitizers_test.py -@@ -0,0 +1,57 @@ -+# -*- coding: utf-8 -*- -+ -+# Import python libs -+from __future__ import absolute_import -+from salt.ext.six import text_type as text -+ -+# Import Salt Libs -+from salt.utils.sanitizers import clean -+ -+# Import Salt Testing Libs -+from salttesting import TestCase, skipIf -+from salttesting.mock import NO_MOCK, NO_MOCK_REASON -+from salttesting.helpers import ensure_in_syspath -+ -+ensure_in_syspath('../../') -+ -+ -+@skipIf(NO_MOCK, NO_MOCK_REASON) -+class SanitizersTestCase(TestCase): -+ ''' -+ TestCase for sanitizers -+ ''' -+ def test_sanitized_trim(self): -+ ''' -+ Test sanitized input for trimming -+ ''' -+ value = u' sample ' -+ response = clean.trim(value) -+ assert response == 'sample' -+ assert type(response) == text -+ -+ def test_sanitized_filename(self): -+ ''' -+ Test sanitized input for filename -+ ''' -+ value = '/absolute/path/to/the/file.txt' -+ response = clean.filename(value) -+ assert response == 'file.txt' -+ -+ value = '../relative/path/to/the/file.txt' -+ response = clean.filename(value) -+ assert response == 'file.txt' -+ -+ def test_sanitized_hostname(self): -+ ''' -+ Test sanitized input for hostname (id) -+ ''' -+ value = ' ../ ../some/dubious/hostname ' -+ response = clean.hostname(value) -+ assert response == 'somedubioushostname' -+ -+ test_sanitized_id = test_sanitized_hostname -+ -+ -+if __name__ == '__main__': -+ from integration import run_tests -+ run_tests(SanitizersTestCase, needs_daemon=False) --- -2.11.0 - - diff --git a/fix-regression-in-file.get_managed-add-unit-tests.patch b/fix-regression-in-file.get_managed-add-unit-tests.patch new file mode 100644 index 0000000..1764a68 --- /dev/null +++ b/fix-regression-in-file.get_managed-add-unit-tests.patch @@ -0,0 +1,196 @@ +From 89fd1a83d282a10728077a08466627271a052733 Mon Sep 17 00:00:00 2001 +From: Erik Johnson +Date: Wed, 1 Mar 2017 10:19:33 -0600 +Subject: [PATCH] Fix regression in file.get_managed, add unit tests + +This is no longer needed since we're invoking the state module directly +and not via the state compiler. + +* Fix regression in file.get_managed when skip_verify=True +* Add integration tests for remote file sources +* Remove next(iter()) extraction +--- + salt/modules/file.py | 6 +-- + salt/states/archive.py | 11 ---- + tests/integration/states/file.py | 105 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 108 insertions(+), 14 deletions(-) + +diff --git a/salt/modules/file.py b/salt/modules/file.py +index 8f0c6914b6..381800bc1a 100644 +--- a/salt/modules/file.py ++++ b/salt/modules/file.py +@@ -3745,13 +3745,13 @@ def get_managed( + if cached_dest and (source_hash or skip_verify): + htype = source_sum.get('hash_type', 'sha256') + cached_sum = get_hash(cached_dest, form=htype) +- if cached_sum != source_sum['hsum']: +- cache_refetch = True +- elif skip_verify: ++ if skip_verify: + # prev: if skip_verify or cached_sum == source_sum['hsum']: + # but `cached_sum == source_sum['hsum']` is elliptical as prev if + sfn = cached_dest + source_sum = {'hsum': cached_sum, 'hash_type': htype} ++ elif cached_sum != source_sum['hsum']: ++ cache_refetch = True + + # If we didn't have the template or remote file, let's get it + # Similarly when the file has been updated and the cache has to be refreshed +diff --git a/salt/states/archive.py b/salt/states/archive.py +index c5df213620..46146e971e 100644 +--- a/salt/states/archive.py ++++ b/salt/states/archive.py +@@ -897,17 +897,6 @@ def extracted(name, + ret['comment'] = '\n'.join([str(x) for x in file_result]) + return ret + +- # Get actual state result. The state.single return is a single-element +- # dictionary with the state's unique ID at the top level, and its value +- # being the state's return dictionary. next(iter(dict_name)) will give +- # us the value of the first key, so +- # file_result[next(iter(file_result))] will give us the results of the +- # state.single we just ran. +- try: +- file_result = file_result[next(iter(file_result))] +- except AttributeError: +- pass +- + try: + if not file_result['result']: + log.debug('failed to download {0}'.format(source_match)) +diff --git a/tests/integration/states/file.py b/tests/integration/states/file.py +index d63f318064..faa83d00e8 100644 +--- a/tests/integration/states/file.py ++++ b/tests/integration/states/file.py +@@ -9,15 +9,22 @@ from __future__ import absolute_import + from distutils.version import LooseVersion + import errno + import glob ++import logging + import os + import re + import sys + import shutil ++import socket + import stat + import tempfile + import textwrap ++import threading ++import tornado.ioloop ++import tornado.web + import filecmp + ++log = logging.getLogger(__name__) ++ + # Import 3rd-party libs + from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin + +@@ -2392,6 +2399,104 @@ class FileTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn): + if check_file: + self.run_function('file.remove', [file]) + ++ ++PORT = 9999 ++FILE_SOURCE = 'http://localhost:{0}/grail/scene33'.format(PORT) ++FILE_HASH = 'd2feb3beb323c79fc7a0f44f1408b4a3' ++STATE_DIR = os.path.join(integration.FILES, 'file', 'base') ++ ++ ++class RemoteFileTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn): ++ ''' ++ Uses a local tornado webserver to test http(s) file.managed states with and ++ without skip_verify ++ ''' ++ @classmethod ++ def webserver(cls): ++ ''' ++ method to start tornado static web app ++ ''' ++ application = tornado.web.Application([ ++ (r'/(.*)', tornado.web.StaticFileHandler, {'path': STATE_DIR}) ++ ]) ++ application.listen(PORT) ++ tornado.ioloop.IOLoop.instance().start() ++ ++ @classmethod ++ def setUpClass(cls): ++ ''' ++ start tornado app on thread and wait until it is running ++ ''' ++ cls.server_thread = threading.Thread(target=cls.webserver) ++ cls.server_thread.daemon = True ++ cls.server_thread.start() ++ # check if tornado app is up ++ port_closed = True ++ while port_closed: ++ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++ result = sock.connect_ex(('127.0.0.1', PORT)) ++ if result == 0: ++ port_closed = False ++ ++ @classmethod ++ def tearDownClass(cls): ++ tornado.ioloop.IOLoop.instance().stop() ++ cls.server_thread.join() ++ ++ def setUp(self): ++ fd_, self.name = tempfile.mkstemp(dir=integration.TMP) ++ try: ++ os.close(fd_) ++ except OSError as exc: ++ if exc.errno != errno.EBADF: ++ raise exc ++ # Remove the file that mkstemp just created so that the states can test ++ # creating a new file instead of a diff from a zero-length file. ++ self.tearDown() ++ ++ def tearDown(self): ++ try: ++ os.remove(self.name) ++ except OSError as exc: ++ if exc.errno != errno.ENOENT: ++ raise exc ++ ++ def test_file_managed_http_source_no_hash(self): ++ ''' ++ Test a remote file with no hash ++ ''' ++ ret = self.run_state('file.managed', ++ name=self.name, ++ source=FILE_SOURCE, ++ skip_verify=False) ++ log.debug('ret = %s', ret) ++ # This should fail because no hash was provided ++ self.assertSaltFalseReturn(ret) ++ ++ def test_file_managed_http_source(self): ++ ''' ++ Test a remote file with no hash ++ ''' ++ ret = self.run_state('file.managed', ++ name=self.name, ++ source=FILE_SOURCE, ++ source_hash=FILE_HASH, ++ skip_verify=False) ++ log.debug('ret = %s', ret) ++ self.assertSaltTrueReturn(ret) ++ ++ def test_file_managed_http_source_skip_verify(self): ++ ''' ++ Test a remote file using skip_verify ++ ''' ++ ret = self.run_state('file.managed', ++ name=self.name, ++ source=FILE_SOURCE, ++ skip_verify=True) ++ log.debug('ret = %s', ret) ++ self.assertSaltTrueReturn(ret) ++ ++ + if __name__ == '__main__': + from integration import run_tests + run_tests(FileTest) +-- +2.11.0 + + diff --git a/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch b/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch deleted file mode 100644 index c5ad91d..0000000 --- a/fix-timezone-handling-for-rpm-installtime-bsc-101707.patch +++ /dev/null @@ -1,34 +0,0 @@ -From f45b229c9fea4be349484d8d041c735fa78500b5 Mon Sep 17 00:00:00 2001 -From: Frantisek Kobzik -Date: Thu, 12 Jan 2017 16:02:47 +0100 -Subject: [PATCH] Fix timezone handling for rpm installtime (bsc#1017078) - -Previously datetime.fromtimestamp was used. If used without additional -parameters, this method returns date in the local timezone. - -Our code took the result of fromtimestamp, appended 'Z' and returned -this string. This is wrong as 'Z' means UTC (the client code parses this -value as UTC, but it's in fact local time). - -Fixed by using utcfromtimestamp. ---- - salt/modules/rpm.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py -index 68268dea1d..9dace3cb6f 100644 ---- a/salt/modules/rpm.py -+++ b/salt/modules/rpm.py -@@ -575,7 +575,7 @@ def info(*packages, **attr): - # Convert Unix ticks into ISO time format - if key in ['build_date', 'install_date']: - try: -- pkg_data[key] = datetime.datetime.fromtimestamp(int(value)).isoformat() + "Z" -+ pkg_data[key] = datetime.datetime.utcfromtimestamp(int(value)).isoformat() + "Z" - except ValueError: - log.warning('Could not convert "{0}" into Unix time'.format(value)) - continue --- -2.11.0 - - diff --git a/increasing-timeouts-for-running-integrations-tests.patch b/increasing-timeouts-for-running-integrations-tests.patch deleted file mode 100644 index 9d2da84..0000000 --- a/increasing-timeouts-for-running-integrations-tests.patch +++ /dev/null @@ -1,170 +0,0 @@ -From 1c22c72afce15585070829054e3460a61947e256 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Mon, 23 Jan 2017 12:10:22 +0000 -Subject: [PATCH] Increasing timeouts for running integrations tests - ---- - tests/integration/__init__.py | 16 ++++++++-------- - tests/integration/runners/state.py | 2 +- - tests/integration/shell/call.py | 6 +++--- - tests/integration/shell/key.py | 2 +- - tests/integration/shell/matcher.py | 2 +- - tests/integration/shell/runner.py | 2 +- - 6 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py -index a37ad61d2e..104566363a 100644 ---- a/tests/integration/__init__.py -+++ b/tests/integration/__init__.py -@@ -801,7 +801,7 @@ class TestDaemon(object): - ) - sys.stdout.flush() - process.start() -- process.wait_until_running(timeout=15) -+ process.wait_until_running(timeout=60) - sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) -@@ -1863,14 +1863,14 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi - except OSError: - os.chdir(INTEGRATION_TEST_DIR) - -- def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=30): # pylint: disable=W0221 -+ def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=60): # pylint: disable=W0221 - ''' - Execute salt - ''' - arg_str = '-c {0} {1}'.format(self.get_config_dir(), arg_str) - return self.run_script('salt', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=timeout) - -- def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, timeout=25): # pylint: disable=W0221 -+ def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, timeout=60): # pylint: disable=W0221 - ''' - Execute salt-ssh - ''' -@@ -1885,7 +1885,7 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi - arg_str, - timeout=timeout, - async_flag=' --async' if async else '') -- return self.run_script('salt-run', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) -+ return self.run_script('salt-run', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) - - def run_run_plus(self, fun, *arg, **kwargs): - ''' -@@ -1932,7 +1932,7 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi - arg_str, - catch_stderr=catch_stderr, - with_retcode=with_retcode, -- timeout=30 -+ timeout=60 - ) - - def run_cp(self, arg_str, with_retcode=False, catch_stderr=False): -@@ -1940,16 +1940,16 @@ class ShellCase(AdaptedConfigurationTestCaseMixIn, ShellTestCase, ScriptPathMixi - Execute salt-cp - ''' - arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str) -- return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) -+ return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) - - def run_call(self, arg_str, with_retcode=False, catch_stderr=False): - ''' - Execute salt-call. - ''' - arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str) -- return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=30) -+ return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=60) - -- def run_cloud(self, arg_str, catch_stderr=False, timeout=15): -+ def run_cloud(self, arg_str, catch_stderr=False, timeout=30): - ''' - Execute salt-cloud - ''' -diff --git a/tests/integration/runners/state.py b/tests/integration/runners/state.py -index 07cfe15fe0..3ca74e915b 100644 ---- a/tests/integration/runners/state.py -+++ b/tests/integration/runners/state.py -@@ -109,7 +109,7 @@ class OrchEventTest(integration.ShellCase): - Tests for orchestration events - ''' - def setUp(self): -- self.timeout = 15 -+ self.timeout = 60 - self.master_d_dir = os.path.join(self.get_config_dir(), 'master.d') - try: - os.makedirs(self.master_d_dir) -diff --git a/tests/integration/shell/call.py b/tests/integration/shell/call.py -index 275878f7ca..e444743720 100644 ---- a/tests/integration/shell/call.py -+++ b/tests/integration/shell/call.py -@@ -272,7 +272,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S - '--config-dir {0} --local cmd.run "echo foo"'.format( - config_dir - ), -- timeout=15 -+ timeout=60 - ) - try: - self.assertIn('local:', ret) -@@ -295,7 +295,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S - '--config-dir {0} cmd.run "echo foo"'.format( - config_dir - ), -- timeout=15 -+ timeout=60 - ) - self.assertIn('local:', ret) - finally: -@@ -325,7 +325,7 @@ class CallTest(integration.ShellCase, testprogram.TestProgramCase, integration.S - '--config-dir {0} cmd.run "echo foo"'.format( - config_dir - ), -- timeout=15, -+ timeout=60, - catch_stderr=True, - with_retcode=True - ) -diff --git a/tests/integration/shell/key.py b/tests/integration/shell/key.py -index bf13fff9a3..aeba671829 100644 ---- a/tests/integration/shell/key.py -+++ b/tests/integration/shell/key.py -@@ -265,7 +265,7 @@ class KeyTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn): - '--config-dir {0} -L'.format( - config_dir - ), -- timeout=15 -+ timeout=60 - ) - try: - self.assertIn('minion', '\n'.join(ret)) -diff --git a/tests/integration/shell/matcher.py b/tests/integration/shell/matcher.py -index e1623ae1b2..2bc8e61220 100644 ---- a/tests/integration/shell/matcher.py -+++ b/tests/integration/shell/matcher.py -@@ -353,7 +353,7 @@ class MatchTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn): - '--config-dir {0} minion test.ping'.format( - config_dir - ), -- timeout=15, -+ timeout=60, - catch_stderr=True, - with_retcode=True - ) -diff --git a/tests/integration/shell/runner.py b/tests/integration/shell/runner.py -index 6d65d537b5..cf7257b93d 100644 ---- a/tests/integration/shell/runner.py -+++ b/tests/integration/shell/runner.py -@@ -109,7 +109,7 @@ class RunTest(integration.ShellCase, testprogram.TestProgramCase, integration.Sh - '--config-dir {0} -d'.format( - config_dir - ), -- timeout=15, -+ timeout=60, - catch_stderr=True, - with_retcode=True - ) --- -2.11.0 - - diff --git a/prevent-crash-on-unknown-to-the-repo-package.patch b/prevent-crash-on-unknown-to-the-repo-package.patch deleted file mode 100644 index 5fa33ed..0000000 --- a/prevent-crash-on-unknown-to-the-repo-package.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 48a8dd97be9ddef45f249d530b9c7d1a593e5ac0 Mon Sep 17 00:00:00 2001 -From: Bo Maryniuk -Date: Fri, 27 Jan 2017 14:31:50 +0100 -Subject: [PATCH] Prevent crash on unknown to the repo package - ---- - salt/states/pkg.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/salt/states/pkg.py b/salt/states/pkg.py -index d81f0c00c7..7c3b27b9ac 100644 ---- a/salt/states/pkg.py -+++ b/salt/states/pkg.py -@@ -1909,7 +1909,7 @@ def latest( - for pkg in desired_pkgs: - if not avail.get(pkg): - # Package either a) is up-to-date, or b) does not exist -- if not cur[pkg]: -+ if not cur.get(pkg): - # Package does not exist - msg = 'No information found for \'{0}\'.'.format(pkg) - log.error(msg) --- -2.11.0 - - diff --git a/prevents-oserror-exception-in-case-path-doesn-t-exis.patch b/prevents-oserror-exception-in-case-path-doesn-t-exis.patch deleted file mode 100644 index a0248b7..0000000 --- a/prevents-oserror-exception-in-case-path-doesn-t-exis.patch +++ /dev/null @@ -1,28 +0,0 @@ -From a74360a87e11bbcd65d253426c930769f83361e4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Tue, 14 Feb 2017 16:13:32 +0000 -Subject: [PATCH] Prevents 'OSError' exception in case path doesn't exist - ---- - salt/returners/local_cache.py | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py -index e2d2979241..d46cb1e70c 100644 ---- a/salt/returners/local_cache.py -+++ b/salt/returners/local_cache.py -@@ -60,6 +60,9 @@ def _walk_through(job_dir): - for top in os.listdir(job_dir): - t_path = os.path.join(job_dir, top) - -+ if not os.path.exists(t_path): -+ continue -+ - for final in os.listdir(t_path): - load_path = os.path.join(t_path, final, LOAD_P) - --- -2.11.0 - - diff --git a/salt-2016.11.2.tar.gz b/salt-2016.11.2.tar.gz deleted file mode 100644 index 2182004..0000000 --- a/salt-2016.11.2.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5c3d3cf4293d5b80a93790c76dec61421991c9c54222abd7327b3437ad13a43 -size 9114042 diff --git a/salt-2016.11.3.tar.gz b/salt-2016.11.3.tar.gz new file mode 100644 index 0000000..32c9ee7 --- /dev/null +++ b/salt-2016.11.3.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d5849f38a858288ebc6ef790ced86ae724e61b06e3ee27e6cecf3f6c1ecbc51 +size 9163351 diff --git a/salt.changes b/salt.changes index c6aa374..2ba91c5 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,34 @@ +------------------------------------------------------------------- +Fri Mar 3 09:36:17 UTC 2017 - bmaryniuk@suse.com + +- Update to 2016.11.3 + See https://docs.saltstack.com/en/develop/topics/releases/2016.11.3.html + for full changelog + +- Use correct package names for PyYAML and MarkupSafe (tbechtold@suse.com) + +- Changed: + * add-ssh-option-to-salt-ssh.patch + +- Added: + * add-options-for-dockerng.patch + * fix-case-in-os_family-for-suse.patch + * fix-regression-in-file.get_managed-add-unit-tests.patch + * translate-variable-arguments-if-they-contain-hidden-.patch + +- Removed: + * add-buildargs-option-to-dockerng.build.patch + * docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch + * fix-a-typo-for-master-comments.patch + * fix-error-parsing.patch + * fix-leading-dots-on-sanitized-hostname.patch + * fix-timezone-handling-for-rpm-installtime-bsc-101707.patch + * increasing-timeouts-for-running-integrations-tests.patch + * prevent-crash-on-unknown-to-the-repo-package.patch + * prevents-oserror-exception-in-case-path-doesn-t-exis.patch + * snapper-module-improvements.patch + * support-dryrun-for-dockerng.sls_build.patch + ------------------------------------------------------------------- Thu Feb 16 14:41:48 UTC 2017 - bmaryniuk@suse.com diff --git a/salt.spec b/salt.spec index ef8102b..d0d9c71 100644 --- a/salt.spec +++ b/salt.spec @@ -37,7 +37,7 @@ %bcond_with builddocs Name: salt -Version: 2016.11.2 +Version: 2016.11.3 Release: 0 Summary: A parallel remote execution system License: Apache-2.0 @@ -51,22 +51,21 @@ Source3: html.tar.bz2 Source4: update-documentation.sh Source5: travis.yml -# We do not upstream this patch because this is something that we have to fix on our side # PATCH-FIX-OPENSUSE use-forking-daemon.patch tserong@suse.com -- We don't have python-systemd, so notify can't work +# We do not upstream this patch because this is something that we have to fix on our side Patch1: tserong-suse.com-we-don-t-have-python-systemd-so-not.patch -# We do not upstream this patch because this is suse custom configuration # PATCH-FIX-OPENSUSE use-salt-user-for-master.patch -- Run salt master as dedicated salt user +# We do not upstream this patch because this is suse custom configuration Patch2: run-salt-master-as-dedicated-salt-user.patch -# (see: https://trello.com/c/wh96lCD4/1528-get-rid-of-0003-check-if-byte-strings-are-properly-encoded-in-utf-8-patch-in-the-salt-package) -# We do not upstream this patch because it has been fixed upstream # PATCH-FIX-OPENSUSE https://github.com/saltstack/salt/pull/30424 +# We do not upstream this patch because it has been fixed upstream +# (see: https://trello.com/c/wh96lCD4/1528-get-rid-of-0003-check-if-byte-strings-are-properly-encoded-in-utf-8-patch-in-the-salt-package) Patch3: check-if-byte-strings-are-properly-encoded-in-utf-8.patch -# We do not upstream this patch because the issue is on our side # PATCH-FIX-OPENSUSE prevent rebuilds in OBS +# We do not upstream this patch because the issue is on our side Patch4: do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch -# We do not upstream this because this is for SUSE only (15.08.2016) # PATCH-FIX-OPENSUSE Generate events from the Salt minion, -# if Zypper has been used outside the Salt infrastructure +# We do not upstream this because this is for SUSE only (15.08.2016) if Zypper has been used outside the Salt infrastructure Patch5: add-zypp-notify-plugin.patch # PATCH-FIX_OPENSUSE Patch6: run-salt-api-as-user-salt-bsc-990029.patch @@ -82,30 +81,16 @@ Patch10: avoid-failures-on-sles-12-sp2-because-of-new-systemd.patch Patch11: add-yum-plugin.patch # PATCH-FIX_OPENSUSE Patch12: add-ssh-option-to-salt-ssh.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38639/commits -Patch13: fix-a-typo-for-master-comments.patch # PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38806 -Patch14: add-a-salt-minion-service-control-file.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38824 -Patch15: add-buildargs-option-to-dockerng.build.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38885 -Patch16: increasing-timeouts-for-running-integrations-tests.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38957 -Patch17: fix-timezone-handling-for-rpm-installtime-bsc-101707.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38940/commits -Patch18: fix-leading-dots-on-sanitized-hostname.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/38991 -Patch19: prevent-crash-on-unknown-to-the-repo-package.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39111 -Patch20: docker-buildargs-require-minimal-dockerpy-1.6.0-and-.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39179 -Patch21: fix-error-parsing.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39162 -Patch22: snapper-module-improvements.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39278 -Patch23: support-dryrun-for-dockerng.sls_build.patch -# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39400 -Patch24: prevents-oserror-exception-in-case-path-doesn-t-exis.patch +Patch13: add-a-salt-minion-service-control-file.patch +# PATCH-FIX-OPENSUSE +Patch14: add-options-for-dockerng.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39591 +Patch15: fix-case-in-os_family-for-suse.patch +# PATCH-FIX_OPENSUSE +Patch16: translate-variable-arguments-if-they-contain-hidden-.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/39762 +Patch17: fix-regression-in-file.get_managed-add-unit-tests.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: logrotate @@ -117,13 +102,13 @@ BuildRequires: python-jinja2 %else BuildRequires: python-Jinja2 %endif +BuildRequires: python-MarkupSafe +BuildRequires: python-PyYAML BuildRequires: python-futures >= 2.0 -BuildRequires: python-markupsafe BuildRequires: python-msgpack-python > 0.3 BuildRequires: python-psutil BuildRequires: python-requests >= 1.0.0 BuildRequires: python-tornado >= 4.2.1 -BuildRequires: python-yaml # requirements/opt.txt (not all) # BuildRequires: python-MySQL-python # BuildRequires: python-timelib @@ -182,13 +167,13 @@ Requires: yum %else Requires: python-Jinja2 %endif +Requires: python-MarkupSafe +Requires: python-PyYAML Requires: python-futures >= 2.0 -Requires: python-markupsafe Requires: python-msgpack-python > 0.3 Requires: python-psutil Requires: python-requests >= 1.0.0 Requires: python-tornado >= 4.2.1 -Requires: python-yaml %if 0%{?suse_version} # required for zypper.py Requires: rpm-python @@ -497,13 +482,6 @@ cp %{S:5} ./.travis.yml %patch15 -p1 %patch16 -p1 %patch17 -p1 -%patch18 -p1 -%patch19 -p1 -%patch20 -p1 -%patch21 -p1 -%patch22 -p1 -%patch23 -p1 -%patch24 -p1 %build %{__python} setup.py --salt-transport=both build diff --git a/snapper-module-improvements.patch b/snapper-module-improvements.patch deleted file mode 100644 index f0f6964..0000000 --- a/snapper-module-improvements.patch +++ /dev/null @@ -1,275 +0,0 @@ -From 678de7117211fc359c9aa7e29f6c2fecf0944b08 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Fri, 27 Jan 2017 17:07:25 +0000 -Subject: [PATCH] Snapper module improvements - -* Snapper: Adding support for deleting snapshots -* Snapper: Adding support for snapshot metadata modification -* Snapper: Adding support for creating configurations -* Adds 'snapper.delete_snapshots' unit tests -* Adds 'snapper.modify_snapshots' unit tests -* Adds 'snapper.create_config' unit tests -* Removing extra spaces -* pylint fixes ---- - salt/modules/snapper.py | 159 +++++++++++++++++++++++++++++++++++-- - tests/unit/modules/snapper_test.py | 50 ++++++++++++ - 2 files changed, 201 insertions(+), 8 deletions(-) - -diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py -index 318ce9b99d..d5f1181743 100644 ---- a/salt/modules/snapper.py -+++ b/salt/modules/snapper.py -@@ -290,6 +290,60 @@ def get_config(name='root'): - ) - - -+def create_config(name=None, -+ subvolume=None, -+ fstype=None, -+ template=None, -+ extra_opts=None): -+ ''' -+ Creates a new Snapper configuration -+ -+ name -+ Name of the new Snapper configuration. -+ subvolume -+ Path to the related subvolume. -+ fstype -+ Filesystem type of the subvolume. -+ template -+ Configuration template to use. (Default: default) -+ extra_opts -+ Extra Snapper configuration opts dictionary. It will override the values provided -+ by the given template (if any). -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs -+ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs template="default" -+ salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs extra_opts='{"NUMBER_CLEANUP": False}' -+ ''' -+ def raise_arg_error(argname): -+ raise CommandExecutionError( -+ 'You must provide a "{0}" for the new configuration'.format(argname) -+ ) -+ -+ if not name: -+ raise_arg_error("name") -+ if not subvolume: -+ raise_arg_error("subvolume") -+ if not fstype: -+ raise_arg_error("fstype") -+ if not template: -+ template = "" -+ -+ try: -+ snapper.CreateConfig(name, subvolume, fstype, template) -+ if extra_opts: -+ set_config(name, **extra_opts) -+ return get_config(name) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError( -+ 'Error encountered while creating the new configuration: {0}' -+ .format(_dbus_exception_to_reason(exc, locals())) -+ ) -+ -+ - def create_snapshot(config='root', snapshot_type='single', pre_number=None, - description=None, cleanup_algorithm='number', userdata=None, - **kwargs): -@@ -309,14 +363,14 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, - cleanup_algorithm - Set the cleanup algorithm for the snapshot. - -- number -- Deletes old snapshots when a certain number of snapshots -- is reached. -- timeline -- Deletes old snapshots but keeps a number of hourly, -- daily, weekly, monthly and yearly snapshots. -- empty-pre-post -- Deletes pre/post snapshot pairs with empty diffs. -+ number -+ Deletes old snapshots when a certain number of snapshots -+ is reached. -+ timeline -+ Deletes old snapshots but keeps a number of hourly, -+ daily, weekly, monthly and yearly snapshots. -+ empty-pre-post -+ Deletes pre/post snapshot pairs with empty diffs. - userdata - Set userdata for the snapshot (key-value pairs). - -@@ -364,6 +418,95 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, - return new_nr - - -+def delete_snapshot(snapshots_ids=None, config="root"): -+ ''' -+ Deletes an snapshot -+ -+ config -+ Configuration name. (Default: root) -+ -+ snapshots_ids -+ List of the snapshots IDs to be deleted. -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.delete_snapshot 54 -+ salt '*' snapper.delete_snapshot config=root 54 -+ salt '*' snapper.delete_snapshot config=root snapshots_ids=[54,55,56] -+ ''' -+ if not snapshots_ids: -+ raise CommandExecutionError('Error: No snapshot ID has been provided') -+ try: -+ current_snapshots_ids = [x['id'] for x in list_snapshots(config)] -+ if not isinstance(snapshots_ids, list): -+ snapshots_ids = [snapshots_ids] -+ if not set(snapshots_ids).issubset(set(current_snapshots_ids)): -+ raise CommandExecutionError( -+ "Error: Snapshots '{0}' not found".format(", ".join( -+ [str(x) for x in set(snapshots_ids).difference( -+ set(current_snapshots_ids))])) -+ ) -+ snapper.DeleteSnapshots(config, snapshots_ids) -+ return {config: {"ids": snapshots_ids, "status": "deleted"}} -+ except dbus.DBusException as exc: -+ raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) -+ -+ -+def modify_snapshot(snapshot_id=None, -+ description=None, -+ userdata=None, -+ cleanup=None, -+ config="root"): -+ ''' -+ Modify attributes of an existing snapshot. -+ -+ config -+ Configuration name. (Default: root) -+ -+ snapshot_id -+ ID of the snapshot to be modified. -+ -+ cleanup -+ Change the cleanup method of the snapshot. (str) -+ -+ description -+ Change the description of the snapshot. (str) -+ -+ userdata -+ Change the userdata dictionary of the snapshot. (dict) -+ -+ CLI example: -+ -+ .. code-block:: bash -+ -+ salt '*' snapper.modify_snapshot 54 description="my snapshot description" -+ salt '*' snapper.modify_snapshot 54 description="my snapshot description" -+ salt '*' snapper.modify_snapshot 54 userdata='{"foo": "bar"}' -+ salt '*' snapper.modify_snapshot snapshot_id=54 cleanup="number" -+ ''' -+ if not snapshot_id: -+ raise CommandExecutionError('Error: No snapshot ID has been provided') -+ -+ snapshot = get_snapshot(config=config, number=snapshot_id) -+ try: -+ # Updating only the explicitely provided attributes by the user -+ updated_opts = { -+ 'description': description if description is not None else snapshot['description'], -+ 'cleanup': cleanup if cleanup is not None else snapshot['cleanup'], -+ 'userdata': userdata if userdata is not None else snapshot['userdata'], -+ } -+ snapper.SetSnapshot(config, -+ snapshot_id, -+ updated_opts['description'], -+ updated_opts['cleanup'], -+ updated_opts['userdata']) -+ return get_snapshot(config=config, number=snapshot_id) -+ except dbus.DBusException as exc: -+ raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) -+ -+ - def _get_num_interval(config, num_pre, num_post): - ''' - Returns numerical interval based on optionals num_pre, num_post values -diff --git a/tests/unit/modules/snapper_test.py b/tests/unit/modules/snapper_test.py -index ca985cfd05..a5d9b7686e 100644 ---- a/tests/unit/modules/snapper_test.py -+++ b/tests/unit/modules/snapper_test.py -@@ -202,6 +202,26 @@ class SnapperTestCase(TestCase): - self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"]) - self.assertEqual(snapper.status_to_string(256), ["ACL info changed"]) - -+ @patch('salt.modules.snapper.snapper.CreateConfig', MagicMock()) -+ @patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])) -+ def test_create_config(self): -+ opts = { -+ 'name': 'testconfig', -+ 'subvolume': '/foo/bar/', -+ 'fstype': 'btrfs', -+ 'template': 'mytemplate', -+ 'extra_opts': {"NUMBER_CLEANUP": False}, -+ } -+ with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: -+ self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) -+ set_config_mock.assert_called_with("testconfig", **opts['extra_opts']) -+ -+ with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: -+ del opts['extra_opts'] -+ self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) -+ assert not set_config_mock.called -+ self.assertRaises(CommandExecutionError, snapper.create_config) -+ - @patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234)) - @patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234)) - @patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234)) -@@ -216,6 +236,36 @@ class SnapperTestCase(TestCase): - } - self.assertEqual(snapper.create_snapshot(**opts), 1234) - -+ @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) -+ @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) -+ def test_delete_snapshot_id_success(self): -+ self.assertEqual(snapper.delete_snapshot(snapshots_ids=43), {"root": {"ids": [43], "status": "deleted"}}) -+ self.assertEqual(snapper.delete_snapshot(snapshots_ids=[42, 43]), {"root": {"ids": [42, 43], "status": "deleted"}}) -+ -+ @patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()) -+ @patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])) -+ def test_delete_snapshot_id_fail(self): -+ self.assertRaises(CommandExecutionError, snapper.delete_snapshot) -+ self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1) -+ self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]) -+ -+ @patch('salt.modules.snapper.snapper.SetSnapshot', MagicMock()) -+ def test_modify_snapshot(self): -+ _ret = { -+ 'userdata': {'userdata2': 'uservalue2'}, -+ 'description': 'UPDATED DESCRIPTION', 'timestamp': 1457006571, -+ 'cleanup': 'number', 'user': 'root', 'type': 'pre', 'id': 42 -+ } -+ _opts = { -+ 'config': 'root', -+ 'snapshot_id': 42, -+ 'cleanup': 'number', -+ 'description': 'UPDATED DESCRIPTION', -+ 'userdata': {'userdata2': 'uservalue2'}, -+ } -+ with patch('salt.modules.snapper.get_snapshot', MagicMock(side_effect=[DBUS_RET['ListSnapshots'][0], _ret])): -+ self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret) -+ - @patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})) - def test__get_num_interval(self): - self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access --- -2.11.0 - - diff --git a/translate-variable-arguments-if-they-contain-hidden-.patch b/translate-variable-arguments-if-they-contain-hidden-.patch new file mode 100644 index 0000000..6a723e6 --- /dev/null +++ b/translate-variable-arguments-if-they-contain-hidden-.patch @@ -0,0 +1,91 @@ +From 7313bf5574a72557a6389b9a991316d0b2c6f848 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +Date: Wed, 1 Mar 2017 15:37:04 +0100 +Subject: [PATCH] translate variable arguments if they contain hidden + keywords (bsc#1025896) + +- includes a test +--- + salt/states/module.py | 30 ++++++++++++++++++++++-------- + tests/unit/states/module_test.py | 14 ++++++++++++++ + 2 files changed, 36 insertions(+), 8 deletions(-) + +diff --git a/salt/states/module.py b/salt/states/module.py +index 686546832f..adc6e12c9d 100644 +--- a/salt/states/module.py ++++ b/salt/states/module.py +@@ -218,16 +218,30 @@ def run(name, **kwargs): + ret['result'] = False + return ret + +- if aspec.varargs and aspec.varargs in kwargs: +- varargs = kwargs.pop(aspec.varargs) ++ if aspec.varargs: ++ if aspec.varargs == 'name': ++ rarg = 'm_name' ++ elif aspec.varargs == 'fun': ++ rarg = 'm_fun' ++ elif aspec.varargs == 'names': ++ rarg = 'm_names' ++ elif aspec.varargs == 'state': ++ rarg = 'm_state' ++ elif aspec.varargs == 'saltenv': ++ rarg = 'm_saltenv' ++ else: ++ rarg = aspec.varargs + +- if not isinstance(varargs, list): +- msg = "'{0}' must be a list." +- ret['comment'] = msg.format(aspec.varargs) +- ret['result'] = False +- return ret ++ if rarg in kwargs: ++ varargs = kwargs.pop(rarg) ++ ++ if not isinstance(varargs, list): ++ msg = "'{0}' must be a list." ++ ret['comment'] = msg.format(aspec.varargs) ++ ret['result'] = False ++ return ret + +- args.extend(varargs) ++ args.extend(varargs) + + nkwargs = {} + if aspec.keywords and aspec.keywords in kwargs: +diff --git a/tests/unit/states/module_test.py b/tests/unit/states/module_test.py +index 0c025e3861..20dda73938 100644 +--- a/tests/unit/states/module_test.py ++++ b/tests/unit/states/module_test.py +@@ -38,6 +38,10 @@ class ModuleStateTest(TestCase): + varargs=None, + keywords=None, + defaults=False) ++ bspec = ArgSpec(args=[], ++ varargs='names', ++ keywords='kwargs', ++ defaults=None) + + def test_module_run_module_not_available(self): + ''' +@@ -69,6 +73,16 @@ class ModuleStateTest(TestCase): + comment = 'The following arguments are missing: world hello' + self.assertEqual(ret['comment'], comment) + ++ @patch('salt.utils.args.get_function_argspec', MagicMock(return_value=bspec)) ++ def test_module_run_hidden_varargs(self): ++ ''' ++ Tests the return of module.run state when hidden varargs are used with ++ wrong type. ++ ''' ++ ret = module.run(CMD, m_names = 'anyname') ++ comment = "'names' must be a list." ++ self.assertEqual(ret['comment'], comment) ++ + + if __name__ == '__main__': + from integration import run_tests +-- +2.11.0 + +