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-options-for-dockerng.patch b/add-options-for-dockerng.patch new file mode 100644 index 0000000..02bd0c8 --- /dev/null +++ b/add-options-for-dockerng.patch @@ -0,0 +1,252 @@ +From 95d547d3e6c98f927eaad77346b84b2ccef95627 Mon Sep 17 00:00:00 2001 +From: Michael Calmer +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 | 51 ++++++++++++++++++----- + tests/unit/modules/dockerng_test.py | 82 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 122 insertions(+), 11 deletions(-) + +diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py +index 29af703ffa..c199fede1e 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.]+)' + +@@ -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 +- 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: +@@ -5865,7 +5873,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. + +@@ -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: + ++ 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 +@@ -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 +- 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/add-ssh-option-to-salt-ssh.patch b/add-ssh-option-to-salt-ssh.patch new file mode 100644 index 0000000..5b9e3fb --- /dev/null +++ b/add-ssh-option-to-salt-ssh.patch @@ -0,0 +1,163 @@ +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 + +--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 23ec948fe0..bbef9d8de1 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( +@@ -693,6 +696,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 +@@ -749,7 +753,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 f78cb623e1..613660fe34 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 + # ssh , but scp [ +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/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-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/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/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.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-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..2ba91c5 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,94 @@ +------------------------------------------------------------------- +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 + +- 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..d0d9c71 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.3 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 @@ -53,61 +53,62 @@ 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 +Patch1: 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 # We do not upstream this patch because this is suse custom configuration -Patch2: 0002-Run-salt-master-as-dedicated-salt-user.patch +Patch2: 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 # (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 +Patch3: 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 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 +Patch4: do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch # 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 +# 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 -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/38806 +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 BuildRequires: python BuildRequires: python-devel # requirements/base.txt +%if 0%{?rhel} +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 @@ -160,14 +161,19 @@ Requires: python Requires: python-certifi %endif # requirements/base.txt +%if 0%{?rhel} +Requires: python-jinja2 +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 @@ -217,6 +223,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 +237,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 +247,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 +273,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 +305,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 +325,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 +347,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 +370,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 +391,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 +464,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 @@ -507,6 +519,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 +545,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 +622,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 +639,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 +896,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 +945,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 +985,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/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 + + 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 +