Accepting request 431193 from systemsmanagement:saltstack
1 OBS-URL: https://build.opensuse.org/request/show/431193 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/salt?expand=0&rev=61
This commit is contained in:
commit
a3840b9cc1
@ -1,7 +1,7 @@
|
|||||||
From f9dbfde1c3e7782d78f6b0b2b6b564f61749941f Mon Sep 17 00:00:00 2001
|
From 3f8257601cb1224221e10e712c0377254714f6fc Mon Sep 17 00:00:00 2001
|
||||||
From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= <kkaempf@suse.de>
|
From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= <kkaempf@suse.de>
|
||||||
Date: Wed, 20 Jan 2016 11:00:15 +0100
|
Date: Wed, 20 Jan 2016 11:00:15 +0100
|
||||||
Subject: [PATCH 01/12] tserong@suse.com -- We don't have python-systemd, so
|
Subject: [PATCH 01/13] tserong@suse.com -- We don't have python-systemd, so
|
||||||
notify can't work
|
notify can't work
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -9,7 +9,7 @@ Subject: [PATCH 01/12] tserong@suse.com -- We don't have python-systemd, so
|
|||||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||||
|
|
||||||
diff --git a/pkg/salt-master.service b/pkg/salt-master.service
|
diff --git a/pkg/salt-master.service b/pkg/salt-master.service
|
||||||
index 0eadf88..2b0f326 100644
|
index 0eadf88a3837..2b0f32623571 100644
|
||||||
--- a/pkg/salt-master.service
|
--- a/pkg/salt-master.service
|
||||||
+++ b/pkg/salt-master.service
|
+++ b/pkg/salt-master.service
|
||||||
@@ -4,8 +4,7 @@ After=network.target
|
@@ -4,8 +4,7 @@ After=network.target
|
||||||
@ -23,5 +23,5 @@ index 0eadf88..2b0f326 100644
|
|||||||
KillMode=process
|
KillMode=process
|
||||||
|
|
||||||
--
|
--
|
||||||
2.1.4
|
2.8.3
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
From af193a109fcae502c4cdd47507aea9f67d809b4b Mon Sep 17 00:00:00 2001
|
From e86b0bf279c7faea457dfb8152fe9f5c829e42be Mon Sep 17 00:00:00 2001
|
||||||
From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= <kkaempf@suse.de>
|
From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= <kkaempf@suse.de>
|
||||||
Date: Wed, 20 Jan 2016 11:01:06 +0100
|
Date: Wed, 20 Jan 2016 11:01:06 +0100
|
||||||
Subject: [PATCH 02/12] Run salt master as dedicated salt user
|
Subject: [PATCH 02/13] Run salt master as dedicated salt user
|
||||||
|
|
||||||
---
|
---
|
||||||
conf/master | 3 ++-
|
conf/master | 3 ++-
|
||||||
@ -9,7 +9,7 @@ Subject: [PATCH 02/12] Run salt master as dedicated salt user
|
|||||||
2 files changed, 5 insertions(+), 1 deletion(-)
|
2 files changed, 5 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
diff --git a/conf/master b/conf/master
|
diff --git a/conf/master b/conf/master
|
||||||
index aae46ef..064828a 100644
|
index 1bbeb50c51b4..54d5f8f3ca75 100644
|
||||||
--- a/conf/master
|
--- a/conf/master
|
||||||
+++ b/conf/master
|
+++ b/conf/master
|
||||||
@@ -25,7 +25,8 @@
|
@@ -25,7 +25,8 @@
|
||||||
@ -23,7 +23,7 @@ index aae46ef..064828a 100644
|
|||||||
# The port used by the communication interface. The ret (return) port is the
|
# The port used by the communication interface. The ret (return) port is the
|
||||||
# interface used for the file server, authentication, job returns, etc.
|
# interface used for the file server, authentication, job returns, etc.
|
||||||
diff --git a/pkg/salt-common.logrotate b/pkg/salt-common.logrotate
|
diff --git a/pkg/salt-common.logrotate b/pkg/salt-common.logrotate
|
||||||
index 3cd0023..8d970c0 100644
|
index 3cd002308e83..8d970c0a64d0 100644
|
||||||
--- a/pkg/salt-common.logrotate
|
--- a/pkg/salt-common.logrotate
|
||||||
+++ b/pkg/salt-common.logrotate
|
+++ b/pkg/salt-common.logrotate
|
||||||
@@ -1,4 +1,5 @@
|
@@ -1,4 +1,5 @@
|
||||||
@ -49,5 +49,5 @@ index 3cd0023..8d970c0 100644
|
|||||||
missingok
|
missingok
|
||||||
rotate 7
|
rotate 7
|
||||||
--
|
--
|
||||||
2.1.4
|
2.8.3
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
From 6035aef0c80ae12a068bee7613c5b7f7f48aa9d3 Mon Sep 17 00:00:00 2001
|
From cad9f1a8fda2a4d8c666abcf74e03e7c6a8eb6be Mon Sep 17 00:00:00 2001
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
Date: Mon, 18 Jan 2016 16:28:48 +0100
|
Date: Mon, 18 Jan 2016 16:28:48 +0100
|
||||||
Subject: [PATCH 03/12] Check if byte strings are properly encoded in UTF-8
|
Subject: [PATCH 03/12] Check if byte strings are properly encoded in UTF-8
|
||||||
@ -9,10 +9,10 @@ Rename keywords arguments variable to a default name.
|
|||||||
1 file changed, 6 insertions(+), 5 deletions(-)
|
1 file changed, 6 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
index fecb671..27b00d5 100644
|
index 7dd73dd..92c604e 100644
|
||||||
--- a/salt/modules/zypper.py
|
--- a/salt/modules/zypper.py
|
||||||
+++ b/salt/modules/zypper.py
|
+++ b/salt/modules/zypper.py
|
||||||
@@ -164,9 +164,9 @@ def info_installed(*names, **kwargs):
|
@@ -335,9 +335,9 @@ def info_installed(*names, **kwargs):
|
||||||
summary, description.
|
summary, description.
|
||||||
|
|
||||||
:param errors:
|
:param errors:
|
||||||
@ -25,7 +25,7 @@ index fecb671..27b00d5 100644
|
|||||||
|
|
||||||
Valid attributes are:
|
Valid attributes are:
|
||||||
ignore, report
|
ignore, report
|
||||||
@@ -179,7 +179,8 @@ def info_installed(*names, **kwargs):
|
@@ -350,7 +350,8 @@ def info_installed(*names, **kwargs):
|
||||||
salt '*' pkg.info_installed <package1> <package2> <package3> ...
|
salt '*' pkg.info_installed <package1> <package2> <package3> ...
|
||||||
salt '*' pkg.info_installed <package1> attr=version,vendor
|
salt '*' pkg.info_installed <package1> attr=version,vendor
|
||||||
salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor
|
salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor
|
||||||
@ -35,7 +35,7 @@ index fecb671..27b00d5 100644
|
|||||||
'''
|
'''
|
||||||
ret = dict()
|
ret = dict()
|
||||||
for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, **kwargs).items():
|
for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, **kwargs).items():
|
||||||
@@ -190,7 +191,7 @@ def info_installed(*names, **kwargs):
|
@@ -361,7 +362,7 @@ def info_installed(*names, **kwargs):
|
||||||
# Check, if string is encoded in a proper UTF-8
|
# Check, if string is encoded in a proper UTF-8
|
||||||
value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore')
|
value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore')
|
||||||
if value != value_:
|
if value != value_:
|
||||||
@ -45,5 +45,5 @@ index fecb671..27b00d5 100644
|
|||||||
if key == 'source_rpm':
|
if key == 'source_rpm':
|
||||||
t_nfo['source'] = value
|
t_nfo['source'] = value
|
||||||
--
|
--
|
||||||
2.1.4
|
2.9.2
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
From a2ffa8e54f3cd8dba3c4b73cad086a6b93fb3a41 Mon Sep 17 00:00:00 2001
|
From a831380a6d7f323c9824d26d6f6e5966b10cbc6e Mon Sep 17 00:00:00 2001
|
||||||
From: Michael Calmer <mc@suse.de>
|
From: Michael Calmer <mc@suse.de>
|
||||||
Date: Fri, 4 Mar 2016 09:51:22 +0100
|
Date: Fri, 4 Mar 2016 09:51:22 +0100
|
||||||
Subject: [PATCH 04/12] do not generate a date in a comment to prevent rebuilds
|
Subject: [PATCH 04/13] do not generate a date in a comment to prevent rebuilds
|
||||||
(bsc#969407)
|
(bsc#969407)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -9,10 +9,10 @@ Subject: [PATCH 04/12] do not generate a date in a comment to prevent rebuilds
|
|||||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||||
|
|
||||||
diff --git a/setup.py b/setup.py
|
diff --git a/setup.py b/setup.py
|
||||||
index 742eae5..d2dd8f7 100755
|
index c84c19824a6f..819ec2b875d1 100755
|
||||||
--- a/setup.py
|
--- a/setup.py
|
||||||
+++ b/setup.py
|
+++ b/setup.py
|
||||||
@@ -605,8 +605,7 @@ class Clean(clean):
|
@@ -618,8 +618,7 @@ class Clean(clean):
|
||||||
|
|
||||||
|
|
||||||
INSTALL_VERSION_TEMPLATE = '''\
|
INSTALL_VERSION_TEMPLATE = '''\
|
||||||
@ -23,5 +23,5 @@ index 742eae5..d2dd8f7 100755
|
|||||||
from salt.version import SaltStackVersion
|
from salt.version import SaltStackVersion
|
||||||
|
|
||||||
--
|
--
|
||||||
2.1.4
|
2.8.3
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
From d5fc00efc2f73018c4c6bf3bea03648dfd1340fc Mon Sep 17 00:00:00 2001
|
From 03732c6879c45596b0a9a0e6500957ec4f9d8415 Mon Sep 17 00:00:00 2001
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
Date: Thu, 17 Mar 2016 12:30:23 +0100
|
Date: Thu, 17 Mar 2016 12:30:23 +0100
|
||||||
Subject: [PATCH 05/12] Use SHA256 hash type by default
|
Subject: [PATCH 05/12] Use SHA256 hash type by default
|
||||||
@ -10,10 +10,10 @@ Subject: [PATCH 05/12] Use SHA256 hash type by default
|
|||||||
3 files changed, 3 insertions(+), 3 deletions(-)
|
3 files changed, 3 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
diff --git a/conf/master b/conf/master
|
diff --git a/conf/master b/conf/master
|
||||||
index 064828a..5e75b15 100644
|
index 8bbe490..118f53b 100644
|
||||||
--- a/conf/master
|
--- a/conf/master
|
||||||
+++ b/conf/master
|
+++ b/conf/master
|
||||||
@@ -474,7 +474,7 @@ syndic_user: salt
|
@@ -509,7 +509,7 @@ syndic_user: salt
|
||||||
#
|
#
|
||||||
# Prior to changing this value, the master should be stopped and all Salt
|
# Prior to changing this value, the master should be stopped and all Salt
|
||||||
# caches should be cleared.
|
# caches should be cleared.
|
||||||
@ -23,10 +23,10 @@ index 064828a..5e75b15 100644
|
|||||||
# The buffer size in the file server can be adjusted here:
|
# The buffer size in the file server can be adjusted here:
|
||||||
#file_buffer_size: 1048576
|
#file_buffer_size: 1048576
|
||||||
diff --git a/conf/minion b/conf/minion
|
diff --git a/conf/minion b/conf/minion
|
||||||
index b408942..32b0d0a 100644
|
index 3ba8ba0..8e4dbd0 100644
|
||||||
--- a/conf/minion
|
--- a/conf/minion
|
||||||
+++ b/conf/minion
|
+++ b/conf/minion
|
||||||
@@ -451,7 +451,7 @@
|
@@ -542,7 +542,7 @@
|
||||||
#
|
#
|
||||||
# Warning: Prior to changing this value, the minion should be stopped and all
|
# Warning: Prior to changing this value, the minion should be stopped and all
|
||||||
# Salt caches should be cleared.
|
# Salt caches should be cleared.
|
||||||
@ -36,10 +36,10 @@ index b408942..32b0d0a 100644
|
|||||||
# The Salt pillar is searched for locally if file_client is set to local. If
|
# 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
|
# this is the case, and pillar data is defined, then the pillar_roots need to
|
||||||
diff --git a/conf/proxy b/conf/proxy
|
diff --git a/conf/proxy b/conf/proxy
|
||||||
index e6ca631..e697357 100644
|
index 7b835e2..ab500f9 100644
|
||||||
--- a/conf/proxy
|
--- a/conf/proxy
|
||||||
+++ b/conf/proxy
|
+++ b/conf/proxy
|
||||||
@@ -427,7 +427,7 @@
|
@@ -435,7 +435,7 @@
|
||||||
#
|
#
|
||||||
# Warning: Prior to changing this value, the minion should be stopped and all
|
# Warning: Prior to changing this value, the minion should be stopped and all
|
||||||
# Salt caches should be cleared.
|
# Salt caches should be cleared.
|
||||||
@ -49,5 +49,5 @@ index e6ca631..e697357 100644
|
|||||||
# The Salt pillar is searched for locally if file_client is set to local. If
|
# 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
|
# this is the case, and pillar data is defined, then the pillar_roots need to
|
||||||
--
|
--
|
||||||
2.1.4
|
2.9.2
|
||||||
|
|
||||||
|
36
0006-Create-salt-proxy-instantiated-service-file.patch
Normal file
36
0006-Create-salt-proxy-instantiated-service-file.patch
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
From 46476c37aa00d9cbbd0ac58e56b7e1c134d33b13 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian McHugh <mchugh19@hotmail.com>
|
||||||
|
Date: Thu, 10 Mar 2016 13:25:01 -0600
|
||||||
|
Subject: [PATCH 06/13] 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 000000000000..f97120a05589
|
||||||
|
--- /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.8.3
|
||||||
|
|
@ -1,168 +0,0 @@
|
|||||||
From 00600229ac41ae618bf01e8af6e2c0183d924204 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Theo Chatzimichos <tampakrap@gmail.com>
|
|
||||||
Date: Sat, 2 Apr 2016 12:29:04 +0200
|
|
||||||
Subject: [PATCH 06/12] Update to 2015.8.8.2
|
|
||||||
|
|
||||||
upstream released a bunch of fixes on top of 2015.8.8, without creating a new
|
|
||||||
tag and proper release. This commit includes:
|
|
||||||
- https://github.com/saltstack/salt/pull/32135
|
|
||||||
- https://github.com/saltstack/salt/pull/32023
|
|
||||||
- https://github.com/saltstack/salt/pull/32117
|
|
||||||
see https://docs.saltstack.com/en/latest/topics/releases/2015.8.8.html#salt-2015-8-8-2
|
|
||||||
---
|
|
||||||
salt/config.py | 63 ++++++++++++++++++++++++++++--------------------
|
|
||||||
salt/modules/win_dacl.py | 7 +++---
|
|
||||||
2 files changed, 41 insertions(+), 29 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/config.py b/salt/config.py
|
|
||||||
index fe1f572..929e094 100644
|
|
||||||
--- a/salt/config.py
|
|
||||||
+++ b/salt/config.py
|
|
||||||
@@ -63,7 +63,7 @@ FLO_DIR = os.path.join(
|
|
||||||
|
|
||||||
VALID_OPTS = {
|
|
||||||
# The address of the salt master. May be specified as IP address or hostname
|
|
||||||
- 'master': str,
|
|
||||||
+ 'master': (str, list),
|
|
||||||
|
|
||||||
# The TCP/UDP port of the master to connect to in order to listen to publications
|
|
||||||
'master_port': int,
|
|
||||||
@@ -541,7 +541,7 @@ VALID_OPTS = {
|
|
||||||
'file_recv': bool,
|
|
||||||
'file_recv_max_size': int,
|
|
||||||
'file_ignore_regex': list,
|
|
||||||
- 'file_ignore_glob': bool,
|
|
||||||
+ 'file_ignore_glob': list,
|
|
||||||
'fileserver_backend': list,
|
|
||||||
'fileserver_followsymlinks': bool,
|
|
||||||
'fileserver_ignoresymlinks': bool,
|
|
||||||
@@ -833,7 +833,7 @@ DEFAULT_MINION_OPTS = {
|
|
||||||
'file_recv': False,
|
|
||||||
'file_recv_max_size': 100,
|
|
||||||
'file_ignore_regex': [],
|
|
||||||
- 'file_ignore_glob': None,
|
|
||||||
+ 'file_ignore_glob': [],
|
|
||||||
'fileserver_backend': ['roots'],
|
|
||||||
'fileserver_followsymlinks': True,
|
|
||||||
'fileserver_ignoresymlinks': False,
|
|
||||||
@@ -1348,26 +1348,30 @@ def _validate_opts(opts):
|
|
||||||
Check that all of the types of values passed into the config are
|
|
||||||
of the right types
|
|
||||||
'''
|
|
||||||
+ def format_multi_opt(valid_type):
|
|
||||||
+ try:
|
|
||||||
+ num_types = len(valid_type)
|
|
||||||
+ except TypeError:
|
|
||||||
+ # Bare type name won't have a length, return the name of the type
|
|
||||||
+ # passed.
|
|
||||||
+ return valid_type.__name__
|
|
||||||
+ else:
|
|
||||||
+ if num_types == 1:
|
|
||||||
+ return valid_type.__name__
|
|
||||||
+ elif num_types > 1:
|
|
||||||
+ ret = ', '.join(x.__name__ for x in valid_type[:-1])
|
|
||||||
+ ret += ' or ' + valid_type[-1].__name__
|
|
||||||
+
|
|
||||||
errors = []
|
|
||||||
- err = ('Key {0} with value {1} has an invalid type of {2}, a {3} is '
|
|
||||||
+
|
|
||||||
+ err = ('Key \'{0}\' with value {1} has an invalid type of {2}, a {3} is '
|
|
||||||
'required for this value')
|
|
||||||
for key, val in six.iteritems(opts):
|
|
||||||
if key in VALID_OPTS:
|
|
||||||
- if isinstance(VALID_OPTS[key](), list):
|
|
||||||
- if isinstance(val, VALID_OPTS[key]):
|
|
||||||
- continue
|
|
||||||
- else:
|
|
||||||
- errors.append(
|
|
||||||
- err.format(key, val, type(val).__name__, 'list')
|
|
||||||
- )
|
|
||||||
- if isinstance(VALID_OPTS[key](), dict):
|
|
||||||
- if isinstance(val, VALID_OPTS[key]):
|
|
||||||
- continue
|
|
||||||
- else:
|
|
||||||
- errors.append(
|
|
||||||
- err.format(key, val, type(val).__name__, 'dict')
|
|
||||||
- )
|
|
||||||
- else:
|
|
||||||
+ if isinstance(val, VALID_OPTS[key]):
|
|
||||||
+ continue
|
|
||||||
+
|
|
||||||
+ if hasattr(VALID_OPTS[key], '__call__'):
|
|
||||||
try:
|
|
||||||
VALID_OPTS[key](val)
|
|
||||||
if isinstance(val, (list, dict)):
|
|
||||||
@@ -1384,14 +1388,21 @@ def _validate_opts(opts):
|
|
||||||
VALID_OPTS[key].__name__
|
|
||||||
)
|
|
||||||
)
|
|
||||||
- except ValueError:
|
|
||||||
+ except (TypeError, ValueError):
|
|
||||||
errors.append(
|
|
||||||
- err.format(key, val, type(val).__name__, VALID_OPTS[key])
|
|
||||||
- )
|
|
||||||
- except TypeError:
|
|
||||||
- errors.append(
|
|
||||||
- err.format(key, val, type(val).__name__, VALID_OPTS[key])
|
|
||||||
+ err.format(key,
|
|
||||||
+ val,
|
|
||||||
+ type(val).__name__,
|
|
||||||
+ VALID_OPTS[key].__name__)
|
|
||||||
)
|
|
||||||
+ continue
|
|
||||||
+
|
|
||||||
+ errors.append(
|
|
||||||
+ err.format(key,
|
|
||||||
+ val,
|
|
||||||
+ type(val).__name__,
|
|
||||||
+ format_multi_opt(VALID_OPTS[key].__name__))
|
|
||||||
+ )
|
|
||||||
|
|
||||||
# RAET on Windows uses 'win32file.CreateMailslot()' for IPC. Due to this,
|
|
||||||
# sock_dirs must start with '\\.\mailslot\' and not contain any colons.
|
|
||||||
@@ -1404,7 +1415,7 @@ def _validate_opts(opts):
|
|
||||||
'\\\\.\\mailslot\\' + opts['sock_dir'].replace(':', ''))
|
|
||||||
|
|
||||||
for error in errors:
|
|
||||||
- log.warning(error)
|
|
||||||
+ log.debug(error)
|
|
||||||
if errors:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
diff --git a/salt/modules/win_dacl.py b/salt/modules/win_dacl.py
|
|
||||||
index d57bb7b..d9ee27a 100644
|
|
||||||
--- a/salt/modules/win_dacl.py
|
|
||||||
+++ b/salt/modules/win_dacl.py
|
|
||||||
@@ -44,9 +44,10 @@ class daclConstants(object):
|
|
||||||
# in ntsecuritycon has the extra bits 0x200 enabled.
|
|
||||||
# Note that you when you set this permission what you'll generally get back is it
|
|
||||||
# ORed with 0x200 (SI_NO_ACL_PROTECT), which is what ntsecuritycon incorrectly defines.
|
|
||||||
- FILE_ALL_ACCESS = (ntsecuritycon.STANDARD_RIGHTS_REQUIRED | ntsecuritycon.SYNCHRONIZE | 0x1ff)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
+ self.FILE_ALL_ACCESS = (ntsecuritycon.STANDARD_RIGHTS_REQUIRED | ntsecuritycon.SYNCHRONIZE | 0x1ff)
|
|
||||||
+
|
|
||||||
self.hkeys_security = {
|
|
||||||
'HKEY_LOCAL_MACHINE': 'MACHINE',
|
|
||||||
'HKEY_USERS': 'USERS',
|
|
||||||
@@ -88,7 +89,7 @@ class daclConstants(object):
|
|
||||||
ntsecuritycon.DELETE,
|
|
||||||
'TEXT': 'modify'},
|
|
||||||
'FULLCONTROL': {
|
|
||||||
- 'BITS': daclConstants.FILE_ALL_ACCESS,
|
|
||||||
+ 'BITS': self.FILE_ALL_ACCESS,
|
|
||||||
'TEXT': 'full control'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -368,7 +369,7 @@ def add_ace(path, objectType, user, permission, acetype, propagation):
|
|
||||||
path: path to the object (i.e. c:\\temp\\file, HKEY_LOCAL_MACHINE\\SOFTWARE\\KEY, etc)
|
|
||||||
user: user to add
|
|
||||||
permission: permissions for the user
|
|
||||||
- acetypes: either allow/deny for each user/permission (ALLOW, DENY)
|
|
||||||
+ acetype: either allow/deny for each user/permission (ALLOW, DENY)
|
|
||||||
propagation: how the ACE applies to children for Registry Keys and Directories(KEY, KEY&SUBKEYS, SUBKEYS)
|
|
||||||
|
|
||||||
CLI Example:
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
221
0007-Add-zypp-notify-plugin.patch
Normal file
221
0007-Add-zypp-notify-plugin.patch
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
From c0aacf83fa51015fb6e50ab96204a7b3c31413a8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
|
Date: Mon, 9 May 2016 10:33:44 +0200
|
||||||
|
Subject: [PATCH 07/21] Add zypp-notify plugin
|
||||||
|
|
||||||
|
* Add unit test to the libzypp drift detector plugin
|
||||||
|
---
|
||||||
|
scripts/zypper/plugins/commit/README.md | 3 ++
|
||||||
|
scripts/zypper/plugins/commit/zyppnotify | 59 +++++++++++++++++++++++++++++
|
||||||
|
tests/unit/zypp_plugins_test.py | 51 +++++++++++++++++++++++++
|
||||||
|
tests/zypp_plugin.py | 64 ++++++++++++++++++++++++++++++++
|
||||||
|
4 files changed, 177 insertions(+)
|
||||||
|
create mode 100644 scripts/zypper/plugins/commit/README.md
|
||||||
|
create mode 100755 scripts/zypper/plugins/commit/zyppnotify
|
||||||
|
create mode 100644 tests/unit/zypp_plugins_test.py
|
||||||
|
create mode 100644 tests/zypp_plugin.py
|
||||||
|
|
||||||
|
diff --git a/scripts/zypper/plugins/commit/README.md b/scripts/zypper/plugins/commit/README.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..01c8917
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/scripts/zypper/plugins/commit/README.md
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+# Zypper plugins
|
||||||
|
+
|
||||||
|
+Plugins here are required to interact with SUSE Manager in conjunction of SaltStack and Zypper.
|
||||||
|
diff --git a/scripts/zypper/plugins/commit/zyppnotify b/scripts/zypper/plugins/commit/zyppnotify
|
||||||
|
new file mode 100755
|
||||||
|
index 0000000..268298b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/scripts/zypper/plugins/commit/zyppnotify
|
||||||
|
@@ -0,0 +1,59 @@
|
||||||
|
+#!/usr/bin/python
|
||||||
|
+#
|
||||||
|
+# Copyright (c) 2016 SUSE Linux LLC
|
||||||
|
+# All Rights Reserved.
|
||||||
|
+#
|
||||||
|
+# Author: Bo Maryniuk <bo@suse.de>
|
||||||
|
+
|
||||||
|
+import sys
|
||||||
|
+import os
|
||||||
|
+import hashlib
|
||||||
|
+
|
||||||
|
+from zypp_plugin import Plugin
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class DriftDetector(Plugin):
|
||||||
|
+ """
|
||||||
|
+ Return diff of the installed packages outside the Salt.
|
||||||
|
+ """
|
||||||
|
+ def __init__(self):
|
||||||
|
+ Plugin.__init__(self)
|
||||||
|
+ self.ck_path = "/var/cache/salt/minion/rpmdb.cookie"
|
||||||
|
+ self.rpm_path = "/var/lib/rpm/Packages"
|
||||||
|
+
|
||||||
|
+ def _get_mtime(self):
|
||||||
|
+ '''
|
||||||
|
+ Get the modified time of the RPM Database.
|
||||||
|
+ Returns:
|
||||||
|
+ Unix ticks
|
||||||
|
+ '''
|
||||||
|
+ return os.path.exists(self.rpm_path) and int(os.path.getmtime(self.rpm_path)) or 0
|
||||||
|
+
|
||||||
|
+ def _get_checksum(self):
|
||||||
|
+ '''
|
||||||
|
+ Get the checksum of the RPM Database.
|
||||||
|
+ Returns:
|
||||||
|
+ hexdigest
|
||||||
|
+ '''
|
||||||
|
+ digest = hashlib.md5()
|
||||||
|
+ with open(self.rpm_path, "rb") as rpm_db_fh:
|
||||||
|
+ while True:
|
||||||
|
+ buff = rpm_db_fh.read(0x1000)
|
||||||
|
+ if not buff:
|
||||||
|
+ break
|
||||||
|
+ digest.update(buff)
|
||||||
|
+
|
||||||
|
+ return digest.hexdigest()
|
||||||
|
+
|
||||||
|
+ def PLUGINEND(self, headers, body):
|
||||||
|
+ """
|
||||||
|
+ Hook when plugin closes Zypper's transaction.
|
||||||
|
+ """
|
||||||
|
+ if 'SALT_RUNNING' not in os.environ:
|
||||||
|
+ with open(self.ck_path, 'w') as ck_fh:
|
||||||
|
+ ck_fh.write('{chksum} {mtime}\n'.format(chksum=self._get_checksum(), mtime=self._get_mtime()))
|
||||||
|
+
|
||||||
|
+ self.ack()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+DriftDetector().main()
|
||||||
|
diff --git a/tests/unit/zypp_plugins_test.py b/tests/unit/zypp_plugins_test.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..550403c
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/unit/zypp_plugins_test.py
|
||||||
|
@@ -0,0 +1,51 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+'''
|
||||||
|
+ :codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
|
||||||
|
+'''
|
||||||
|
+
|
||||||
|
+# Import Python Libs
|
||||||
|
+from __future__ import absolute_import
|
||||||
|
+
|
||||||
|
+# Import Salt Testing Libs
|
||||||
|
+from salttesting.helpers import ensure_in_syspath
|
||||||
|
+from salttesting import TestCase, skipIf
|
||||||
|
+from salttesting.mock import (
|
||||||
|
+ MagicMock,
|
||||||
|
+ patch,
|
||||||
|
+ NO_MOCK,
|
||||||
|
+ NO_MOCK_REASON
|
||||||
|
+)
|
||||||
|
+
|
||||||
|
+ensure_in_syspath('../')
|
||||||
|
+
|
||||||
|
+import os
|
||||||
|
+import imp
|
||||||
|
+from zypp_plugin import BogusIO
|
||||||
|
+
|
||||||
|
+zyppnotify = imp.load_source('zyppnotify', os.path.sep.join(os.path.dirname(__file__).split(
|
||||||
|
+ os.path.sep)[:-2] + ['scripts', 'zypper', 'plugins', 'commit', 'zyppnotify']))
|
||||||
|
+
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
+class ZyppPluginsTestCase(TestCase):
|
||||||
|
+ '''
|
||||||
|
+ Test shipped libzypp plugins.
|
||||||
|
+ '''
|
||||||
|
+ def test_drift_detector(self):
|
||||||
|
+ '''
|
||||||
|
+ Test drift detector for a correct cookie file.
|
||||||
|
+ Returns:
|
||||||
|
+
|
||||||
|
+ '''
|
||||||
|
+ drift = zyppnotify.DriftDetector()
|
||||||
|
+ drift._get_mtime = MagicMock(return_value=123)
|
||||||
|
+ drift._get_checksum = MagicMock(return_value='deadbeef')
|
||||||
|
+ bogus_io = BogusIO()
|
||||||
|
+ with patch('zyppnotify.open', bogus_io):
|
||||||
|
+ drift.PLUGINEND(None, None)
|
||||||
|
+ self.assertEqual(str(bogus_io), 'deadbeef 123\n')
|
||||||
|
+ self.assertEqual(bogus_io.mode, 'w')
|
||||||
|
+ self.assertEqual(bogus_io.path, '/var/cache/salt/minion/rpmdb.cookie')
|
||||||
|
+
|
||||||
|
+if __name__ == '__main__':
|
||||||
|
+ from integration import run_tests
|
||||||
|
+ run_tests(ZyppPluginsTestCase, needs_daemon=False)
|
||||||
|
diff --git a/tests/zypp_plugin.py b/tests/zypp_plugin.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..218f703
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/zypp_plugin.py
|
||||||
|
@@ -0,0 +1,64 @@
|
||||||
|
+'''
|
||||||
|
+Related to zypp_plugins_test.py module.
|
||||||
|
+'''
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class Plugin(object):
|
||||||
|
+ '''
|
||||||
|
+ Bogus module for Zypp Plugins tests.
|
||||||
|
+ '''
|
||||||
|
+ def ack(self):
|
||||||
|
+ '''
|
||||||
|
+ Acknowledge that the plugin had finished the transaction
|
||||||
|
+ Returns:
|
||||||
|
+
|
||||||
|
+ '''
|
||||||
|
+
|
||||||
|
+ def main(self):
|
||||||
|
+ '''
|
||||||
|
+ Register plugin
|
||||||
|
+ Returns:
|
||||||
|
+
|
||||||
|
+ '''
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class BogusIO(object):
|
||||||
|
+ '''
|
||||||
|
+ Read/write logger.
|
||||||
|
+ '''
|
||||||
|
+
|
||||||
|
+ def __init__(self):
|
||||||
|
+ self.content = list()
|
||||||
|
+ self.closed = False
|
||||||
|
+
|
||||||
|
+ def __str__(self):
|
||||||
|
+ return '\n'.join(self.content)
|
||||||
|
+
|
||||||
|
+ def __call__(self, *args, **kwargs):
|
||||||
|
+ self.path, self.mode = args
|
||||||
|
+ return self
|
||||||
|
+
|
||||||
|
+ def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
+ self.close()
|
||||||
|
+
|
||||||
|
+ def __enter__(self):
|
||||||
|
+ return self
|
||||||
|
+
|
||||||
|
+ def write(self, data):
|
||||||
|
+ '''
|
||||||
|
+ Simulate writing data
|
||||||
|
+ Args:
|
||||||
|
+ data:
|
||||||
|
+
|
||||||
|
+ Returns:
|
||||||
|
+
|
||||||
|
+ '''
|
||||||
|
+ self.content.append(data)
|
||||||
|
+
|
||||||
|
+ def close(self):
|
||||||
|
+ '''
|
||||||
|
+ Simulate closing the IO object.
|
||||||
|
+ Returns:
|
||||||
|
+
|
||||||
|
+ '''
|
||||||
|
+ self.closed = True
|
||||||
|
--
|
||||||
|
2.10.0
|
||||||
|
|
@ -1,350 +0,0 @@
|
|||||||
From e3a599712daafb88b6b77ebf6c7684fdd10ffedf Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
|
||||||
Date: Wed, 30 Mar 2016 12:14:21 +0200
|
|
||||||
Subject: [PATCH 07/12] Force-sort the RPM output to ensure latest version of
|
|
||||||
the multi-package on top of the list.
|
|
||||||
|
|
||||||
- Remove version_cmp from the yumpkg and use just a lowpkg alias
|
|
||||||
- Remove version_cmp from Zypper module and use just lowpkg alias
|
|
||||||
- Merge yumpkg's and zypper's version_cmp for a common use
|
|
||||||
- Sort installed pkgs data by version_cmp
|
|
||||||
- Move "string to EVR" function to the utilities
|
|
||||||
- Remove suse/redhat checks, refactor code.
|
|
||||||
- Fix condition from returning None on 0
|
|
||||||
- Remove tests from the zypper_test that belongs to rpm_test
|
|
||||||
- Add lowpkg tests for version comparison
|
|
||||||
- Fix lint
|
|
||||||
- Fix the documentation
|
|
||||||
---
|
|
||||||
salt/modules/rpm.py | 60 +++++++++++++++++++++++++++++++++++++--
|
|
||||||
salt/modules/yumpkg.py | 28 ++----------------
|
|
||||||
salt/modules/zypper.py | 58 +------------------------------------
|
|
||||||
salt/utils/__init__.py | 35 +++++++++++++++++++++++
|
|
||||||
tests/unit/modules/rpm_test.py | 21 ++++++++++++++
|
|
||||||
tests/unit/modules/zypper_test.py | 22 --------------
|
|
||||||
6 files changed, 117 insertions(+), 107 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py
|
|
||||||
index 5d60dd2..6026f18 100644
|
|
||||||
--- a/salt/modules/rpm.py
|
|
||||||
+++ b/salt/modules/rpm.py
|
|
||||||
@@ -17,6 +17,19 @@ import salt.utils.pkg.rpm
|
|
||||||
# pylint: disable=import-error,redefined-builtin
|
|
||||||
from salt.ext.six.moves import shlex_quote as _cmd_quote
|
|
||||||
from salt.ext.six.moves import zip
|
|
||||||
+
|
|
||||||
+try:
|
|
||||||
+ import rpm
|
|
||||||
+ HAS_RPM = True
|
|
||||||
+except ImportError:
|
|
||||||
+ HAS_RPM = False
|
|
||||||
+
|
|
||||||
+try:
|
|
||||||
+ import rpmUtils.miscutils
|
|
||||||
+ HAS_RPMUTILS = True
|
|
||||||
+except ImportError:
|
|
||||||
+ HAS_RPMUTILS = False
|
|
||||||
+
|
|
||||||
# pylint: enable=import-error,redefined-builtin
|
|
||||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
|
||||||
|
|
||||||
@@ -491,7 +504,7 @@ def info(*packages, **attr):
|
|
||||||
else:
|
|
||||||
out = call['stdout']
|
|
||||||
|
|
||||||
- ret = dict()
|
|
||||||
+ _ret = list()
|
|
||||||
for pkg_info in re.split(r"----*", out):
|
|
||||||
pkg_info = pkg_info.strip()
|
|
||||||
if not pkg_info:
|
|
||||||
@@ -538,6 +551,49 @@ def info(*packages, **attr):
|
|
||||||
if attr and 'description' in attr or not attr:
|
|
||||||
pkg_data['description'] = os.linesep.join(descr)
|
|
||||||
if pkg_name:
|
|
||||||
- ret[pkg_name] = pkg_data
|
|
||||||
+ pkg_data['name'] = pkg_name
|
|
||||||
+ _ret.append(pkg_data)
|
|
||||||
+
|
|
||||||
+ # Force-sort package data by version,
|
|
||||||
+ # pick only latest versions
|
|
||||||
+ # (in case multiple packages installed, e.g. kernel)
|
|
||||||
+ ret = dict()
|
|
||||||
+ for pkg_data in reversed(sorted(_ret, cmp=lambda a_vrs, b_vrs: version_cmp(a_vrs['version'], b_vrs['version']))):
|
|
||||||
+ pkg_name = pkg_data.pop('name')
|
|
||||||
+ if pkg_name not in ret:
|
|
||||||
+ ret[pkg_name] = pkg_data.copy()
|
|
||||||
|
|
||||||
return ret
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def version_cmp(ver1, ver2):
|
|
||||||
+ '''
|
|
||||||
+ .. versionadded:: 2015.8.9
|
|
||||||
+
|
|
||||||
+ Do a cmp-style comparison on two packages. Return -1 if ver1 < ver2, 0 if
|
|
||||||
+ ver1 == ver2, and 1 if ver1 > ver2. Return None if there was a problem
|
|
||||||
+ making the comparison.
|
|
||||||
+
|
|
||||||
+ CLI Example:
|
|
||||||
+
|
|
||||||
+ .. code-block:: bash
|
|
||||||
+
|
|
||||||
+ salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002'
|
|
||||||
+ '''
|
|
||||||
+ try:
|
|
||||||
+ if HAS_RPM:
|
|
||||||
+ cmp_func = rpm.labelCompare
|
|
||||||
+ elif HAS_RPMUTILS:
|
|
||||||
+ cmp_func = rpmUtils.miscutils.compareEVR
|
|
||||||
+ else:
|
|
||||||
+ cmp_func = None
|
|
||||||
+ cmp_result = cmp_func is None and 2 or cmp_func(salt.utils.str_version_to_evr(ver1),
|
|
||||||
+ salt.utils.str_version_to_evr(ver2))
|
|
||||||
+ if cmp_result not in (-1, 0, 1):
|
|
||||||
+ raise Exception("Comparison result '{0}' is invalid".format(cmp_result))
|
|
||||||
+
|
|
||||||
+ return cmp_result
|
|
||||||
+ except Exception as exc:
|
|
||||||
+ log.warning("Failed to compare version '{0}' to '{1}' using RPM: {2}".format(ver1, ver2, exc))
|
|
||||||
+
|
|
||||||
+ return salt.utils.version_cmp(ver1, ver2)
|
|
||||||
diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py
|
|
||||||
index 1bfc38d..1cde676 100644
|
|
||||||
--- a/salt/modules/yumpkg.py
|
|
||||||
+++ b/salt/modules/yumpkg.py
|
|
||||||
@@ -40,12 +40,6 @@ try:
|
|
||||||
except ImportError:
|
|
||||||
from salt.ext.six.moves import configparser
|
|
||||||
HAS_YUM = False
|
|
||||||
-
|
|
||||||
-try:
|
|
||||||
- import rpmUtils.miscutils
|
|
||||||
- HAS_RPMUTILS = True
|
|
||||||
-except ImportError:
|
|
||||||
- HAS_RPMUTILS = False
|
|
||||||
# pylint: enable=import-error,redefined-builtin
|
|
||||||
|
|
||||||
# Import salt libs
|
|
||||||
@@ -665,26 +659,8 @@ def version_cmp(pkg1, pkg2):
|
|
||||||
|
|
||||||
salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002'
|
|
||||||
'''
|
|
||||||
- if HAS_RPMUTILS:
|
|
||||||
- try:
|
|
||||||
- cmp_result = rpmUtils.miscutils.compareEVR(
|
|
||||||
- rpmUtils.miscutils.stringToVersion(pkg1),
|
|
||||||
- rpmUtils.miscutils.stringToVersion(pkg2)
|
|
||||||
- )
|
|
||||||
- if cmp_result not in (-1, 0, 1):
|
|
||||||
- raise Exception(
|
|
||||||
- 'cmp result \'{0}\' is invalid'.format(cmp_result)
|
|
||||||
- )
|
|
||||||
- return cmp_result
|
|
||||||
- except Exception as exc:
|
|
||||||
- log.warning(
|
|
||||||
- 'Failed to compare version \'%s\' to \'%s\' using '
|
|
||||||
- 'rpmUtils: %s', pkg1, pkg2, exc
|
|
||||||
- )
|
|
||||||
- # Fall back to distutils.version.LooseVersion (should only need to do
|
|
||||||
- # this for RHEL5, or if an exception is raised when attempting to compare
|
|
||||||
- # using rpmUtils)
|
|
||||||
- return salt.utils.version_cmp(pkg1, pkg2)
|
|
||||||
+
|
|
||||||
+ return __salt__['lowpkg.version_cmp'](pkg1, pkg2)
|
|
||||||
|
|
||||||
|
|
||||||
def list_pkgs(versions_as_list=False, **kwargs):
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
|
||||||
index 27b00d5..63c473c 100644
|
|
||||||
--- a/salt/modules/zypper.py
|
|
||||||
+++ b/salt/modules/zypper.py
|
|
||||||
@@ -17,12 +17,6 @@ import os
|
|
||||||
import salt.ext.six as six
|
|
||||||
from salt.ext.six.moves import configparser
|
|
||||||
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse
|
|
||||||
-
|
|
||||||
-try:
|
|
||||||
- import rpm
|
|
||||||
- HAS_RPM = True
|
|
||||||
-except ImportError:
|
|
||||||
- HAS_RPM = False
|
|
||||||
# pylint: enable=import-error,redefined-builtin,no-name-in-module
|
|
||||||
|
|
||||||
from xml.dom import minidom as dom
|
|
||||||
@@ -347,40 +341,6 @@ def version(*names, **kwargs):
|
|
||||||
return __salt__['pkg_resource.version'](*names, **kwargs) or {}
|
|
||||||
|
|
||||||
|
|
||||||
-def _string_to_evr(verstring):
|
|
||||||
- '''
|
|
||||||
- Split the version string into epoch, version and release and
|
|
||||||
- return this as tuple.
|
|
||||||
-
|
|
||||||
- epoch is always not empty.
|
|
||||||
- version and release can be an empty string if such a component
|
|
||||||
- could not be found in the version string.
|
|
||||||
-
|
|
||||||
- "2:1.0-1.2" => ('2', '1.0', '1.2)
|
|
||||||
- "1.0" => ('0', '1.0', '')
|
|
||||||
- "" => ('0', '', '')
|
|
||||||
- '''
|
|
||||||
- if verstring in [None, '']:
|
|
||||||
- return ('0', '', '')
|
|
||||||
- idx_e = verstring.find(':')
|
|
||||||
- if idx_e != -1:
|
|
||||||
- try:
|
|
||||||
- epoch = str(int(verstring[:idx_e]))
|
|
||||||
- except ValueError:
|
|
||||||
- # look, garbage in the epoch field, how fun, kill it
|
|
||||||
- epoch = '0' # this is our fallback, deal
|
|
||||||
- else:
|
|
||||||
- epoch = '0'
|
|
||||||
- idx_r = verstring.find('-')
|
|
||||||
- if idx_r != -1:
|
|
||||||
- version = verstring[idx_e + 1:idx_r]
|
|
||||||
- release = verstring[idx_r + 1:]
|
|
||||||
- else:
|
|
||||||
- version = verstring[idx_e + 1:]
|
|
||||||
- release = ''
|
|
||||||
- return (epoch, version, release)
|
|
||||||
-
|
|
||||||
-
|
|
||||||
def version_cmp(ver1, ver2):
|
|
||||||
'''
|
|
||||||
.. versionadded:: 2015.5.4
|
|
||||||
@@ -395,23 +355,7 @@ def version_cmp(ver1, ver2):
|
|
||||||
|
|
||||||
salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002'
|
|
||||||
'''
|
|
||||||
- if HAS_RPM:
|
|
||||||
- try:
|
|
||||||
- cmp_result = rpm.labelCompare(
|
|
||||||
- _string_to_evr(ver1),
|
|
||||||
- _string_to_evr(ver2)
|
|
||||||
- )
|
|
||||||
- if cmp_result not in (-1, 0, 1):
|
|
||||||
- raise Exception(
|
|
||||||
- 'cmp result \'{0}\' is invalid'.format(cmp_result)
|
|
||||||
- )
|
|
||||||
- return cmp_result
|
|
||||||
- except Exception as exc:
|
|
||||||
- log.warning(
|
|
||||||
- 'Failed to compare version \'{0}\' to \'{1}\' using '
|
|
||||||
- 'rpmUtils: {2}'.format(ver1, ver2, exc)
|
|
||||||
- )
|
|
||||||
- return salt.utils.version_cmp(ver1, ver2)
|
|
||||||
+ return __salt__['lowpkg.version_cmp'](ver1, ver2)
|
|
||||||
|
|
||||||
|
|
||||||
def list_pkgs(versions_as_list=False, **kwargs):
|
|
||||||
diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py
|
|
||||||
index f83a677..8956a15 100644
|
|
||||||
--- a/salt/utils/__init__.py
|
|
||||||
+++ b/salt/utils/__init__.py
|
|
||||||
@@ -2881,3 +2881,38 @@ def split_input(val):
|
|
||||||
return [x.strip() for x in val.split(',')]
|
|
||||||
except AttributeError:
|
|
||||||
return [x.strip() for x in str(val).split(',')]
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def str_version_to_evr(verstring):
|
|
||||||
+ '''
|
|
||||||
+ Split the package version string into epoch, version and release.
|
|
||||||
+ Return this as tuple.
|
|
||||||
+
|
|
||||||
+ The epoch is always not empty. The version and the release can be an empty
|
|
||||||
+ string if such a component could not be found in the version string.
|
|
||||||
+
|
|
||||||
+ "2:1.0-1.2" => ('2', '1.0', '1.2)
|
|
||||||
+ "1.0" => ('0', '1.0', '')
|
|
||||||
+ "" => ('0', '', '')
|
|
||||||
+ '''
|
|
||||||
+ if verstring in [None, '']:
|
|
||||||
+ return '0', '', ''
|
|
||||||
+
|
|
||||||
+ idx_e = verstring.find(':')
|
|
||||||
+ if idx_e != -1:
|
|
||||||
+ try:
|
|
||||||
+ epoch = str(int(verstring[:idx_e]))
|
|
||||||
+ except ValueError:
|
|
||||||
+ # look, garbage in the epoch field, how fun, kill it
|
|
||||||
+ epoch = '0' # this is our fallback, deal
|
|
||||||
+ else:
|
|
||||||
+ epoch = '0'
|
|
||||||
+ idx_r = verstring.find('-')
|
|
||||||
+ if idx_r != -1:
|
|
||||||
+ version = verstring[idx_e + 1:idx_r]
|
|
||||||
+ release = verstring[idx_r + 1:]
|
|
||||||
+ else:
|
|
||||||
+ version = verstring[idx_e + 1:]
|
|
||||||
+ release = ''
|
|
||||||
+
|
|
||||||
+ return epoch, version, release
|
|
||||||
diff --git a/tests/unit/modules/rpm_test.py b/tests/unit/modules/rpm_test.py
|
|
||||||
index 8bfce9b..f180736 100644
|
|
||||||
--- a/tests/unit/modules/rpm_test.py
|
|
||||||
+++ b/tests/unit/modules/rpm_test.py
|
|
||||||
@@ -95,6 +95,27 @@ class RpmTestCase(TestCase):
|
|
||||||
self.assertDictEqual(rpm.owner('/usr/bin/python', '/usr/bin/vim'),
|
|
||||||
ret)
|
|
||||||
|
|
||||||
+ @patch('salt.modules.rpm.HAS_RPM', True)
|
|
||||||
+ def test_version_cmp_rpm(self):
|
|
||||||
+ '''
|
|
||||||
+ Test package version is called RPM version if RPM-Python is installed
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ rpm.rpm = MagicMock(return_value=MagicMock)
|
|
||||||
+ with patch('salt.modules.rpm.rpm.labelCompare', MagicMock(return_value=0)):
|
|
||||||
+ self.assertEqual(0, rpm.version_cmp('1', '2')) # mock returns 0, which means RPM was called
|
|
||||||
+
|
|
||||||
+ @patch('salt.modules.rpm.HAS_RPM', False)
|
|
||||||
+ def test_version_cmp_fallback(self):
|
|
||||||
+ '''
|
|
||||||
+ Test package version is called RPM version if RPM-Python is installed
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ rpm.rpm = MagicMock(return_value=MagicMock)
|
|
||||||
+ with patch('salt.modules.rpm.rpm.labelCompare', MagicMock(return_value=0)):
|
|
||||||
+ self.assertEqual(-1, rpm.version_cmp('1', '2')) # mock returns -1, a python implementation was called
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from integration import run_tests
|
|
||||||
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
|
||||||
index 5c4eb67..67cf52a 100644
|
|
||||||
--- a/tests/unit/modules/zypper_test.py
|
|
||||||
+++ b/tests/unit/modules/zypper_test.py
|
|
||||||
@@ -301,28 +301,6 @@ class ZypperTestCase(TestCase):
|
|
||||||
self.assertFalse(zypper.upgrade_available(pkg_name))
|
|
||||||
self.assertTrue(zypper.upgrade_available('vim'))
|
|
||||||
|
|
||||||
- @patch('salt.modules.zypper.HAS_RPM', True)
|
|
||||||
- def test_version_cmp_rpm(self):
|
|
||||||
- '''
|
|
||||||
- Test package version is called RPM version if RPM-Python is installed
|
|
||||||
-
|
|
||||||
- :return:
|
|
||||||
- '''
|
|
||||||
- with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
|
|
||||||
- with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
|
|
||||||
- self.assertEqual(0, zypper.version_cmp('1', '2')) # mock returns 0, which means RPM was called
|
|
||||||
-
|
|
||||||
- @patch('salt.modules.zypper.HAS_RPM', False)
|
|
||||||
- def test_version_cmp_fallback(self):
|
|
||||||
- '''
|
|
||||||
- Test package version is called RPM version if RPM-Python is installed
|
|
||||||
-
|
|
||||||
- :return:
|
|
||||||
- '''
|
|
||||||
- with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
|
|
||||||
- with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
|
|
||||||
- self.assertEqual(-1, zypper.version_cmp('1', '2')) # mock returns -1, a python implementation was called
|
|
||||||
-
|
|
||||||
def test_list_pkgs(self):
|
|
||||||
'''
|
|
||||||
Test packages listing.
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
@ -1,922 +0,0 @@
|
|||||||
From 2dcc979ab2897619baebfef5779120a98284d408 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@maryniuk.net>
|
|
||||||
Date: Wed, 6 Apr 2016 20:55:45 +0200
|
|
||||||
Subject: [PATCH 08/12] Cleaner deprecation process with decorators
|
|
||||||
|
|
||||||
* Add deprecation decorator scaffold
|
|
||||||
|
|
||||||
* Capture type error and unhandled exceptions while function calls
|
|
||||||
|
|
||||||
* Aware of the current and future version of deprecation
|
|
||||||
|
|
||||||
* Implement initially is_deprecated decorator
|
|
||||||
|
|
||||||
* Add an alias for the capitalization
|
|
||||||
|
|
||||||
* Fix capitalization easier way
|
|
||||||
|
|
||||||
* Remove an extra line
|
|
||||||
|
|
||||||
* Add successor name to the deprecation decorator.
|
|
||||||
|
|
||||||
* Granulate logging and error messages.
|
|
||||||
|
|
||||||
* Implement function swapper
|
|
||||||
|
|
||||||
* Raise later the caught exception
|
|
||||||
|
|
||||||
* Clarify exception message
|
|
||||||
|
|
||||||
* Save function original name
|
|
||||||
|
|
||||||
* Remove an extra line
|
|
||||||
|
|
||||||
* Hide an alternative hidden function name in the error message, preserving the error itself
|
|
||||||
|
|
||||||
* Rename variable as private
|
|
||||||
|
|
||||||
* Add a method to detect if a function is using its previous version
|
|
||||||
|
|
||||||
* Message to the log and/or raise an exception accordingly to the status of used function
|
|
||||||
|
|
||||||
* Log an error along with the exception
|
|
||||||
|
|
||||||
* Add internal method documentation
|
|
||||||
|
|
||||||
* Add documentation and usage process for decorator "is_deprecated"
|
|
||||||
|
|
||||||
* Add documentation and process usage for the decorator "with_deprecated"
|
|
||||||
|
|
||||||
* Hide private method name
|
|
||||||
|
|
||||||
* Fix PEP8, re-word the error message
|
|
||||||
|
|
||||||
* Deprecate basic uptime function
|
|
||||||
|
|
||||||
* Add initial decorator unit test
|
|
||||||
|
|
||||||
* Rename old/new functions, mock versions
|
|
||||||
|
|
||||||
* Move frequent data to the test setup
|
|
||||||
|
|
||||||
* Add logging on EOL exception
|
|
||||||
|
|
||||||
* Rename and document high to low version test on is_deprecated
|
|
||||||
|
|
||||||
* Implement a test on low to high version of is_deprecated decorator
|
|
||||||
|
|
||||||
* Add a correction to the test description
|
|
||||||
|
|
||||||
* Remove a dead code
|
|
||||||
|
|
||||||
* Implement a test for high to low version on is_deprecated, using with_successor param
|
|
||||||
|
|
||||||
* Correct typso adn mistaeks
|
|
||||||
|
|
||||||
* Implement high to low version with successor param on is_deprecated
|
|
||||||
|
|
||||||
* Setup a virtual name for the module
|
|
||||||
|
|
||||||
* Implement test for with_deprecated should raise an exception if same deprecated function not found
|
|
||||||
|
|
||||||
* Implement test for with_deprecated an old function is picked up if configured
|
|
||||||
|
|
||||||
* Correct test description purpose
|
|
||||||
|
|
||||||
* Implement test with_deprecated when no deprecation is requested
|
|
||||||
|
|
||||||
* Add logging test to the configured deprecation request
|
|
||||||
|
|
||||||
* Add logging testing when deprecated version wasn't requested
|
|
||||||
|
|
||||||
* Implement test EOL for with_deprecated decorator
|
|
||||||
|
|
||||||
* Correct test explanation
|
|
||||||
|
|
||||||
* Rename the test
|
|
||||||
|
|
||||||
* Implement with_deprecated no EOL, deprecated other function name
|
|
||||||
|
|
||||||
* Implement with_deprecated, deprecated other function name, EOL reached
|
|
||||||
|
|
||||||
* Add test description for the with_deprecated + with_name + EOL
|
|
||||||
|
|
||||||
* Fix confusing test names
|
|
||||||
|
|
||||||
* Add logging test to the is_deprecated decorator when function as not found.
|
|
||||||
|
|
||||||
* Add more test point to each test, remove empty lines
|
|
||||||
|
|
||||||
* Bugfix: at certain conditions a wrong alias name is reported to the log
|
|
||||||
|
|
||||||
* Fix a typo in a comment
|
|
||||||
|
|
||||||
* Add test for the logging
|
|
||||||
|
|
||||||
* Disable a pylint: None will _never_ be raised
|
|
||||||
|
|
||||||
* Fix test for the deprecated "status.uptime" version
|
|
||||||
|
|
||||||
* Bugfix: Do not yank raised exceptions
|
|
||||||
|
|
||||||
* Remove unnecessary decorator
|
|
||||||
|
|
||||||
* Add test for the new uptime
|
|
||||||
|
|
||||||
* Add test for the new uptime fails when /proc/uptime does not exists
|
|
||||||
|
|
||||||
* Rename old test case
|
|
||||||
|
|
||||||
* Skip test for the UTC time, unless freeze time is used.
|
|
||||||
|
|
||||||
* Fix pylint
|
|
||||||
|
|
||||||
* Fix documentation
|
|
||||||
|
|
||||||
* Bugfix: proxy-pass the docstring of the decorated function
|
|
||||||
|
|
||||||
* Lint fix
|
|
||||||
---
|
|
||||||
salt/modules/status.py | 40 ++++-
|
|
||||||
salt/utils/decorators/__init__.py | 345 +++++++++++++++++++++++++++++++++++-
|
|
||||||
tests/unit/modules/status_test.py | 48 ++++-
|
|
||||||
tests/unit/utils/decorators_test.py | 232 ++++++++++++++++++++++++
|
|
||||||
4 files changed, 649 insertions(+), 16 deletions(-)
|
|
||||||
create mode 100644 tests/unit/utils/decorators_test.py
|
|
||||||
|
|
||||||
diff --git a/salt/modules/status.py b/salt/modules/status.py
|
|
||||||
index 1e80b36..04c6204 100644
|
|
||||||
--- a/salt/modules/status.py
|
|
||||||
+++ b/salt/modules/status.py
|
|
||||||
@@ -11,6 +11,8 @@ import os
|
|
||||||
import re
|
|
||||||
import fnmatch
|
|
||||||
import collections
|
|
||||||
+import time
|
|
||||||
+import datetime
|
|
||||||
|
|
||||||
# Import 3rd-party libs
|
|
||||||
import salt.ext.six as six
|
|
||||||
@@ -23,6 +25,8 @@ import salt.utils.event
|
|
||||||
from salt.utils.network import host_to_ip as _host_to_ip
|
|
||||||
from salt.utils.network import remote_port_tcp as _remote_port_tcp
|
|
||||||
from salt.ext.six.moves import zip
|
|
||||||
+from salt.utils.decorators import with_deprecated
|
|
||||||
+from salt.exceptions import CommandExecutionError
|
|
||||||
|
|
||||||
__virtualname__ = 'status'
|
|
||||||
__opts__ = {}
|
|
||||||
@@ -30,7 +34,8 @@ __opts__ = {}
|
|
||||||
|
|
||||||
def __virtual__():
|
|
||||||
if salt.utils.is_windows():
|
|
||||||
- return (False, 'Cannot load status module on windows')
|
|
||||||
+ return False, 'Windows platform is not supported by this module'
|
|
||||||
+
|
|
||||||
return __virtualname__
|
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +125,38 @@ def custom():
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
-def uptime(human_readable=True):
|
|
||||||
+@with_deprecated(globals(), "Boron")
|
|
||||||
+def uptime():
|
|
||||||
+ '''
|
|
||||||
+ Return the uptime for this system.
|
|
||||||
+
|
|
||||||
+ CLI Example:
|
|
||||||
+
|
|
||||||
+ .. code-block:: bash
|
|
||||||
+
|
|
||||||
+ salt '*' status.uptime
|
|
||||||
+ '''
|
|
||||||
+ ut_path = "/proc/uptime"
|
|
||||||
+ if not os.path.exists(ut_path):
|
|
||||||
+ raise CommandExecutionError("File {ut_path} was not found.".format(ut_path=ut_path))
|
|
||||||
+
|
|
||||||
+ ut_ret = {
|
|
||||||
+ 'seconds': int(float(open(ut_path).read().strip().split()[0]))
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ utc_time = datetime.datetime.utcfromtimestamp(time.time() - ut_ret['seconds'])
|
|
||||||
+ ut_ret['since_iso'] = utc_time.isoformat()
|
|
||||||
+ ut_ret['since_t'] = time.mktime(utc_time.timetuple())
|
|
||||||
+ ut_ret['days'] = ut_ret['seconds'] / 60 / 60 / 24
|
|
||||||
+ hours = (ut_ret['seconds'] - (ut_ret['days'] * 24 * 60 * 60)) / 60 / 60
|
|
||||||
+ minutes = ((ut_ret['seconds'] - (ut_ret['days'] * 24 * 60 * 60)) / 60) - hours * 60
|
|
||||||
+ ut_ret['time'] = '{0}:{1}'.format(hours, minutes)
|
|
||||||
+ ut_ret['users'] = len(__salt__['cmd.run']("who -s").split(os.linesep))
|
|
||||||
+
|
|
||||||
+ return ut_ret
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def _uptime(human_readable=True):
|
|
||||||
'''
|
|
||||||
Return the uptime for this minion
|
|
||||||
|
|
||||||
diff --git a/salt/utils/decorators/__init__.py b/salt/utils/decorators/__init__.py
|
|
||||||
index 45d3bd6..3b43504 100644
|
|
||||||
--- a/salt/utils/decorators/__init__.py
|
|
||||||
+++ b/salt/utils/decorators/__init__.py
|
|
||||||
@@ -13,7 +13,8 @@ from collections import defaultdict
|
|
||||||
|
|
||||||
# Import salt libs
|
|
||||||
import salt.utils
|
|
||||||
-from salt.exceptions import CommandNotFoundError
|
|
||||||
+from salt.exceptions import CommandNotFoundError, CommandExecutionError
|
|
||||||
+from salt.version import SaltStackVersion, __saltstack_version__
|
|
||||||
from salt.log import LOG_LEVELS
|
|
||||||
|
|
||||||
# Import 3rd-party libs
|
|
||||||
@@ -144,10 +145,7 @@ class Depends(object):
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
-class depends(Depends): # pylint: disable=C0103
|
|
||||||
- '''
|
|
||||||
- Wrapper of Depends for capitalization
|
|
||||||
- '''
|
|
||||||
+depends = Depends
|
|
||||||
|
|
||||||
|
|
||||||
def timing(function):
|
|
||||||
@@ -248,3 +246,340 @@ def memoize(func):
|
|
||||||
cache[args] = func(*args)
|
|
||||||
return cache[args]
|
|
||||||
return _memoize
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class _DeprecationDecorator(object):
|
|
||||||
+ '''
|
|
||||||
+ Base mix-in class for the deprecation decorator.
|
|
||||||
+ Takes care of a common functionality, used in its derivatives.
|
|
||||||
+ '''
|
|
||||||
+
|
|
||||||
+ def __init__(self, globals, version):
|
|
||||||
+ '''
|
|
||||||
+ Constructor.
|
|
||||||
+
|
|
||||||
+ :param globals: Module globals. Important for finding out replacement functions
|
|
||||||
+ :param version: Expiration version
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+
|
|
||||||
+ self._globals = globals
|
|
||||||
+ self._exp_version_name = version
|
|
||||||
+ self._exp_version = SaltStackVersion.from_name(self._exp_version_name)
|
|
||||||
+ self._curr_version = __saltstack_version__.info
|
|
||||||
+ self._options = self._globals['__opts__']
|
|
||||||
+ self._raise_later = None
|
|
||||||
+ self._function = None
|
|
||||||
+ self._orig_f_name = None
|
|
||||||
+
|
|
||||||
+ def _get_args(self, kwargs):
|
|
||||||
+ '''
|
|
||||||
+ Extract function-specific keywords from all of the kwargs.
|
|
||||||
+
|
|
||||||
+ :param kwargs:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ _args = list()
|
|
||||||
+ _kwargs = dict()
|
|
||||||
+
|
|
||||||
+ for arg_item in kwargs.get('__pub_arg', list()):
|
|
||||||
+ if type(arg_item) == dict:
|
|
||||||
+ _kwargs.update(arg_item.copy())
|
|
||||||
+ else:
|
|
||||||
+ _args.append(arg_item)
|
|
||||||
+ return _args, _kwargs
|
|
||||||
+
|
|
||||||
+ def _call_function(self, kwargs):
|
|
||||||
+ '''
|
|
||||||
+ Call target function that has been decorated.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ if self._raise_later:
|
|
||||||
+ raise self._raise_later # pylint: disable=E0702
|
|
||||||
+
|
|
||||||
+ if self._function:
|
|
||||||
+ args, kwargs = self._get_args(kwargs)
|
|
||||||
+ try:
|
|
||||||
+ return self._function(*args, **kwargs)
|
|
||||||
+ except TypeError as error:
|
|
||||||
+ error = str(error).replace(self._function.func_name, self._orig_f_name) # Hide hidden functions
|
|
||||||
+ log.error('Function "{f_name}" was not properly called: {error}'.format(f_name=self._orig_f_name,
|
|
||||||
+ error=error))
|
|
||||||
+ return self._function.__doc__
|
|
||||||
+ except Exception as error:
|
|
||||||
+ log.error('Unhandled exception occurred in '
|
|
||||||
+ 'function "{f_name}: {error}'.format(f_name=self._function.func_name,
|
|
||||||
+ error=error))
|
|
||||||
+ raise error
|
|
||||||
+ else:
|
|
||||||
+ raise CommandExecutionError("Function is deprecated, but the successor function was not found.")
|
|
||||||
+
|
|
||||||
+ def __call__(self, function):
|
|
||||||
+ '''
|
|
||||||
+ Callable method of the decorator object when
|
|
||||||
+ the decorated function is gets called.
|
|
||||||
+
|
|
||||||
+ :param function:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self._function = function
|
|
||||||
+ self._orig_f_name = self._function.func_name
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class _IsDeprecated(_DeprecationDecorator):
|
|
||||||
+ '''
|
|
||||||
+ This decorator should be used only with the deprecated functions
|
|
||||||
+ to mark them as deprecated and alter its behavior a corresponding way.
|
|
||||||
+ The usage is only suitable if deprecation process is renaming
|
|
||||||
+ the function from one to another. In case function name or even function
|
|
||||||
+ signature stays the same, please use 'with_deprecated' decorator instead.
|
|
||||||
+
|
|
||||||
+ It has the following functionality:
|
|
||||||
+
|
|
||||||
+ 1. Put a warning level message to the log, informing that
|
|
||||||
+ the deprecated function has been in use.
|
|
||||||
+
|
|
||||||
+ 2. Raise an exception, if deprecated function is being called,
|
|
||||||
+ but the lifetime of it already expired.
|
|
||||||
+
|
|
||||||
+ 3. Point to the successor of the deprecated function in the
|
|
||||||
+ log messages as well during the blocking it, once expired.
|
|
||||||
+
|
|
||||||
+ Usage of this decorator as follows. In this example no successor
|
|
||||||
+ is mentioned, hence the function "foo()" will be logged with the
|
|
||||||
+ warning each time is called and blocked completely, once EOF of
|
|
||||||
+ it is reached:
|
|
||||||
+
|
|
||||||
+ from salt.util.decorators import is_deprecated
|
|
||||||
+
|
|
||||||
+ @is_deprecated(globals(), "Beryllium")
|
|
||||||
+ def foo():
|
|
||||||
+ pass
|
|
||||||
+
|
|
||||||
+ In the following example a successor function is mentioned, hence
|
|
||||||
+ every time the function "bar()" is called, message will suggest
|
|
||||||
+ to use function "baz()" instead. Once EOF is reached of the function
|
|
||||||
+ "bar()", an exception will ask to use function "baz()", in order
|
|
||||||
+ to continue:
|
|
||||||
+
|
|
||||||
+ from salt.util.decorators import is_deprecated
|
|
||||||
+
|
|
||||||
+ @is_deprecated(globals(), "Beryllium", with_successor="baz")
|
|
||||||
+ def bar():
|
|
||||||
+ pass
|
|
||||||
+
|
|
||||||
+ def baz():
|
|
||||||
+ pass
|
|
||||||
+ '''
|
|
||||||
+
|
|
||||||
+ def __init__(self, globals, version, with_successor=None):
|
|
||||||
+ '''
|
|
||||||
+ Constructor of the decorator 'is_deprecated'.
|
|
||||||
+
|
|
||||||
+ :param globals: Module globals
|
|
||||||
+ :param version: Version to be deprecated
|
|
||||||
+ :param with_successor: Successor function (optional)
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ _DeprecationDecorator.__init__(self, globals, version)
|
|
||||||
+ self._successor = with_successor
|
|
||||||
+
|
|
||||||
+ def __call__(self, function):
|
|
||||||
+ '''
|
|
||||||
+ Callable method of the decorator object when
|
|
||||||
+ the decorated function is gets called.
|
|
||||||
+
|
|
||||||
+ :param function:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ _DeprecationDecorator.__call__(self, function)
|
|
||||||
+
|
|
||||||
+ def _decorate(*args, **kwargs):
|
|
||||||
+ '''
|
|
||||||
+ Decorator function.
|
|
||||||
+
|
|
||||||
+ :param args:
|
|
||||||
+ :param kwargs:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ if self._curr_version < self._exp_version:
|
|
||||||
+ msg = ['The function "{f_name}" is deprecated and will '
|
|
||||||
+ 'expire in version "{version_name}".'.format(f_name=self._function.func_name,
|
|
||||||
+ version_name=self._exp_version_name)]
|
|
||||||
+ if self._successor:
|
|
||||||
+ msg.append('Use successor "{successor}" instead.'.format(successor=self._successor))
|
|
||||||
+ log.warning(' '.join(msg))
|
|
||||||
+ else:
|
|
||||||
+ msg = ['The lifetime of the function "{f_name}" expired.'.format(f_name=self._function.func_name)]
|
|
||||||
+ if self._successor:
|
|
||||||
+ msg.append('Please use its successor "{successor}" instead.'.format(successor=self._successor))
|
|
||||||
+ log.warning(' '.join(msg))
|
|
||||||
+ raise CommandExecutionError(' '.join(msg))
|
|
||||||
+ return self._call_function(kwargs)
|
|
||||||
+ return _decorate
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+is_deprecated = _IsDeprecated
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class _WithDeprecated(_DeprecationDecorator):
|
|
||||||
+ '''
|
|
||||||
+ This decorator should be used with the successor functions
|
|
||||||
+ to mark them as a new and alter its behavior in a corresponding way.
|
|
||||||
+ It is used alone if a function content or function signature
|
|
||||||
+ needs to be replaced, leaving the name of the function same.
|
|
||||||
+ In case function needs to be renamed or just dropped, it has
|
|
||||||
+ to be used in pair with 'is_deprecated' decorator.
|
|
||||||
+
|
|
||||||
+ It has the following functionality:
|
|
||||||
+
|
|
||||||
+ 1. Put a warning level message to the log, in case a component
|
|
||||||
+ is using its deprecated version.
|
|
||||||
+
|
|
||||||
+ 2. Switch between old and new function in case an older version
|
|
||||||
+ is configured for the desired use.
|
|
||||||
+
|
|
||||||
+ 3. Raise an exception, if deprecated version reached EOL and
|
|
||||||
+ point out for the new version.
|
|
||||||
+
|
|
||||||
+ Usage of this decorator as follows. If 'with_name' is not specified,
|
|
||||||
+ then the name of the deprecated function is assumed with the "_" prefix.
|
|
||||||
+ In this case, in order to deprecate a function, it is required:
|
|
||||||
+
|
|
||||||
+ - Add a prefix "_" to an existing function. E.g.: "foo()" to "_foo()".
|
|
||||||
+
|
|
||||||
+ - Implement a new function with exactly the same name, just without
|
|
||||||
+ the prefix "_".
|
|
||||||
+
|
|
||||||
+ Example:
|
|
||||||
+
|
|
||||||
+ from salt.util.decorators import with_deprecated
|
|
||||||
+
|
|
||||||
+ @with_deprecated(globals(), "Beryllium")
|
|
||||||
+ def foo():
|
|
||||||
+ "This is a new function"
|
|
||||||
+
|
|
||||||
+ def _foo():
|
|
||||||
+ "This is a deprecated function"
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ In case there is a need to deprecate a function and rename it,
|
|
||||||
+ the decorator shuld be used with the 'with_name' parameter. This
|
|
||||||
+ parameter is pointing to the existing deprecated function. In this
|
|
||||||
+ case deprecation process as follows:
|
|
||||||
+
|
|
||||||
+ - Leave a deprecated function without changes, as is.
|
|
||||||
+
|
|
||||||
+ - Implement a new function and decorate it with this decorator.
|
|
||||||
+
|
|
||||||
+ - Set a parameter 'with_name' to the deprecated function.
|
|
||||||
+
|
|
||||||
+ - If a new function has a different name than a deprecated,
|
|
||||||
+ decorate a deprecated function with the 'is_deprecated' decorator
|
|
||||||
+ in order to let the function have a deprecated behavior.
|
|
||||||
+
|
|
||||||
+ Example:
|
|
||||||
+
|
|
||||||
+ from salt.util.decorators import with_deprecated
|
|
||||||
+
|
|
||||||
+ @with_deprecated(globals(), "Beryllium", with_name="an_old_function")
|
|
||||||
+ def a_new_function():
|
|
||||||
+ "This is a new function"
|
|
||||||
+
|
|
||||||
+ @is_deprecated(globals(), "Beryllium", with_successor="a_new_function")
|
|
||||||
+ def an_old_function():
|
|
||||||
+ "This is a deprecated function"
|
|
||||||
+
|
|
||||||
+ '''
|
|
||||||
+ MODULE_NAME = '__virtualname__'
|
|
||||||
+ CFG_KEY = 'use_deprecated'
|
|
||||||
+
|
|
||||||
+ def __init__(self, globals, version, with_name=None):
|
|
||||||
+ '''
|
|
||||||
+ Constructor of the decorator 'with_deprecated'
|
|
||||||
+
|
|
||||||
+ :param globals:
|
|
||||||
+ :param version:
|
|
||||||
+ :param with_name:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ _DeprecationDecorator.__init__(self, globals, version)
|
|
||||||
+ self._with_name = with_name
|
|
||||||
+
|
|
||||||
+ def _set_function(self, function):
|
|
||||||
+ '''
|
|
||||||
+ Based on the configuration, set to execute an old or a new function.
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ full_name = "{m_name}.{f_name}".format(m_name=self._globals.get(self.MODULE_NAME, ''),
|
|
||||||
+ f_name=function.func_name)
|
|
||||||
+ if full_name.startswith("."):
|
|
||||||
+ self._raise_later = CommandExecutionError('Module not found for function "{f_name}"'.format(
|
|
||||||
+ f_name=function.func_name))
|
|
||||||
+
|
|
||||||
+ if full_name in self._options.get(self.CFG_KEY, list()):
|
|
||||||
+ self._function = self._globals.get(self._with_name or "_{0}".format(function.func_name))
|
|
||||||
+
|
|
||||||
+ def _is_used_deprecated(self):
|
|
||||||
+ '''
|
|
||||||
+ Returns True, if a component configuration explicitly is
|
|
||||||
+ asking to use an old version of the deprecated function.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ return "{m_name}.{f_name}".format(m_name=self._globals.get(self.MODULE_NAME, ''),
|
|
||||||
+ f_name=self._orig_f_name) in self._options.get(self.CFG_KEY, list())
|
|
||||||
+
|
|
||||||
+ def __call__(self, function):
|
|
||||||
+ '''
|
|
||||||
+ Callable method of the decorator object when
|
|
||||||
+ the decorated function is gets called.
|
|
||||||
+
|
|
||||||
+ :param function:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ _DeprecationDecorator.__call__(self, function)
|
|
||||||
+
|
|
||||||
+ def _decorate(*args, **kwargs):
|
|
||||||
+ '''
|
|
||||||
+ Decorator function.
|
|
||||||
+
|
|
||||||
+ :param args:
|
|
||||||
+ :param kwargs:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self._set_function(function)
|
|
||||||
+ if self._is_used_deprecated():
|
|
||||||
+ if self._curr_version < self._exp_version:
|
|
||||||
+ msg = list()
|
|
||||||
+ if self._with_name:
|
|
||||||
+ msg.append('The function "{f_name}" is deprecated and will '
|
|
||||||
+ 'expire in version "{version_name}".'.format(
|
|
||||||
+ f_name=self._with_name.startswith("_") and self._orig_f_name or self._with_name,
|
|
||||||
+ version_name=self._exp_version_name))
|
|
||||||
+ else:
|
|
||||||
+ msg.append('The function is using its deprecated version and will '
|
|
||||||
+ 'expire in version "{version_name}".'.format(version_name=self._exp_version_name))
|
|
||||||
+ msg.append('Use its successor "{successor}" instead.'.format(successor=self._orig_f_name))
|
|
||||||
+ log.warning(' '.join(msg))
|
|
||||||
+ else:
|
|
||||||
+ msg_patt = 'The lifetime of the function "{f_name}" expired.'
|
|
||||||
+ if '_' + self._orig_f_name == self._function.func_name:
|
|
||||||
+ msg = [msg_patt.format(f_name=self._orig_f_name),
|
|
||||||
+ 'Please turn off its deprecated version in the configuration']
|
|
||||||
+ else:
|
|
||||||
+ msg = ['Although function "{f_name}" is called, an alias "{f_alias}" '
|
|
||||||
+ 'is configured as its deprecated version.'.format(
|
|
||||||
+ f_name=self._orig_f_name, f_alias=self._with_name or self._orig_f_name),
|
|
||||||
+ msg_patt.format(f_name=self._with_name or self._orig_f_name),
|
|
||||||
+ 'Please use its successor "{successor}" instead.'.format(successor=self._orig_f_name)]
|
|
||||||
+ log.error(' '.join(msg))
|
|
||||||
+ raise CommandExecutionError(' '.join(msg))
|
|
||||||
+ return self._call_function(kwargs)
|
|
||||||
+
|
|
||||||
+ _decorate.__doc__ = self._function.__doc__
|
|
||||||
+ return _decorate
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+with_deprecated = _WithDeprecated
|
|
||||||
diff --git a/tests/unit/modules/status_test.py b/tests/unit/modules/status_test.py
|
|
||||||
index 191da09..b5cee4f 100644
|
|
||||||
--- a/tests/unit/modules/status_test.py
|
|
||||||
+++ b/tests/unit/modules/status_test.py
|
|
||||||
@@ -5,15 +5,14 @@ from __future__ import absolute_import
|
|
||||||
|
|
||||||
# Import Salt Libs
|
|
||||||
from salt.modules import status
|
|
||||||
+from salt.exceptions import CommandExecutionError
|
|
||||||
|
|
||||||
# Import Salt Testing Libs
|
|
||||||
-from salttesting import skipIf, TestCase
|
|
||||||
+from salttesting import TestCase
|
|
||||||
from salttesting.helpers import ensure_in_syspath
|
|
||||||
from salttesting.mock import (
|
|
||||||
MagicMock,
|
|
||||||
patch,
|
|
||||||
- NO_MOCK,
|
|
||||||
- NO_MOCK_REASON
|
|
||||||
)
|
|
||||||
|
|
||||||
ensure_in_syspath('../../')
|
|
||||||
@@ -22,36 +21,67 @@ ensure_in_syspath('../../')
|
|
||||||
status.__salt__ = {}
|
|
||||||
|
|
||||||
|
|
||||||
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
||||||
class StatusTestCase(TestCase):
|
|
||||||
'''
|
|
||||||
test modules.status functions
|
|
||||||
'''
|
|
||||||
+
|
|
||||||
def test_uptime(self):
|
|
||||||
'''
|
|
||||||
- test modules.status.uptime function
|
|
||||||
+ Test modules.status.uptime function, new version
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ class ProcUptime(object):
|
|
||||||
+ def __init__(self, *args, **kwargs):
|
|
||||||
+ self.data = "773865.18 1003405.46"
|
|
||||||
+
|
|
||||||
+ def read(self):
|
|
||||||
+ return self.data
|
|
||||||
+
|
|
||||||
+ with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value="1\n2\n3")}):
|
|
||||||
+ with patch('os.path.exists', MagicMock(return_value=True)):
|
|
||||||
+ with patch('time.time', MagicMock(return_value=1458821523.72)):
|
|
||||||
+ status.open = ProcUptime
|
|
||||||
+ u_time = status.uptime()
|
|
||||||
+ self.assertEqual(u_time['users'], 3)
|
|
||||||
+ self.assertEqual(u_time['seconds'], 773865)
|
|
||||||
+ self.assertEqual(u_time['days'], 8)
|
|
||||||
+ self.assertEqual(u_time['time'], '22:57')
|
|
||||||
+
|
|
||||||
+ def test_uptime_failure(self):
|
|
||||||
+ '''
|
|
||||||
+ Test modules.status.uptime function should raise an exception if /proc/uptime does not exists.
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ with patch('os.path.exists', MagicMock(return_value=False)):
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ status.uptime()
|
|
||||||
+
|
|
||||||
+ def test_deprecated_uptime(self):
|
|
||||||
+ '''
|
|
||||||
+ test modules.status.uptime function, deprecated version
|
|
||||||
'''
|
|
||||||
mock_uptime = 'very often'
|
|
||||||
mock_run = MagicMock(return_value=mock_uptime)
|
|
||||||
with patch.dict(status.__salt__, {'cmd.run': mock_run}):
|
|
||||||
- self.assertEqual(status.uptime(), mock_uptime)
|
|
||||||
+ self.assertEqual(status._uptime(), mock_uptime)
|
|
||||||
|
|
||||||
mock_uptime = 'very idle'
|
|
||||||
mock_run = MagicMock(return_value=mock_uptime)
|
|
||||||
with patch.dict(status.__salt__, {'cmd.run': mock_run}):
|
|
||||||
with patch('os.path.exists', MagicMock(return_value=True)):
|
|
||||||
- self.assertEqual(status.uptime(human_readable=False), mock_uptime.split()[0])
|
|
||||||
+ self.assertEqual(status._uptime(human_readable=False), mock_uptime.split()[0])
|
|
||||||
|
|
||||||
mock_uptime = ''
|
|
||||||
mock_return = 'unexpected format in /proc/uptime'
|
|
||||||
mock_run = MagicMock(return_value=mock_uptime)
|
|
||||||
with patch.dict(status.__salt__, {'cmd.run': mock_run}):
|
|
||||||
with patch('os.path.exists', MagicMock(return_value=True)):
|
|
||||||
- self.assertEqual(status.uptime(human_readable=False), mock_return)
|
|
||||||
+ self.assertEqual(status._uptime(human_readable=False), mock_return)
|
|
||||||
|
|
||||||
mock_return = 'cannot find /proc/uptime'
|
|
||||||
with patch('os.path.exists', MagicMock(return_value=False)):
|
|
||||||
- self.assertEqual(status.uptime(human_readable=False), mock_return)
|
|
||||||
+ self.assertEqual(status._uptime(human_readable=False), mock_return)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
diff --git a/tests/unit/utils/decorators_test.py b/tests/unit/utils/decorators_test.py
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..4078340
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/unit/utils/decorators_test.py
|
|
||||||
@@ -0,0 +1,232 @@
|
|
||||||
+# -*- coding: utf-8 -*-
|
|
||||||
+'''
|
|
||||||
+ :codeauthor: :email:`Bo Maryniuk (bo@suse.de)`
|
|
||||||
+ unit.utils.decorators_test
|
|
||||||
+'''
|
|
||||||
+
|
|
||||||
+# Import Python libs
|
|
||||||
+from __future__ import absolute_import
|
|
||||||
+
|
|
||||||
+# Import Salt Testing libs
|
|
||||||
+from salttesting import TestCase
|
|
||||||
+from salttesting.helpers import ensure_in_syspath
|
|
||||||
+from salt.utils import decorators
|
|
||||||
+from salt.version import SaltStackVersion
|
|
||||||
+from salt.exceptions import CommandExecutionError
|
|
||||||
+
|
|
||||||
+ensure_in_syspath('../../')
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class DummyLogger(object):
|
|
||||||
+ '''
|
|
||||||
+ Dummy logger accepts everything and simply logs
|
|
||||||
+ '''
|
|
||||||
+ def __init__(self, messages):
|
|
||||||
+ self._messages = messages
|
|
||||||
+
|
|
||||||
+ def __getattr__(self, item):
|
|
||||||
+ return self._log
|
|
||||||
+
|
|
||||||
+ def _log(self, msg):
|
|
||||||
+ self._messages.append(msg)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class DecoratorsTest(TestCase):
|
|
||||||
+ '''
|
|
||||||
+ Testing decorators.
|
|
||||||
+ '''
|
|
||||||
+ def old_function(self):
|
|
||||||
+ return "old"
|
|
||||||
+
|
|
||||||
+ def new_function(self):
|
|
||||||
+ return "new"
|
|
||||||
+
|
|
||||||
+ def _mk_version(self, name):
|
|
||||||
+ '''
|
|
||||||
+ Make a version
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ return name, SaltStackVersion.from_name(name)
|
|
||||||
+
|
|
||||||
+ def setUp(self):
|
|
||||||
+ '''
|
|
||||||
+ Setup a test
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs = {
|
|
||||||
+ '__virtualname__': 'test',
|
|
||||||
+ '__opts__': {},
|
|
||||||
+ 'old_function': self.old_function,
|
|
||||||
+ 'new_function': self.new_function,
|
|
||||||
+ }
|
|
||||||
+ self.messages = list()
|
|
||||||
+ decorators.log = DummyLogger(self.messages)
|
|
||||||
+
|
|
||||||
+ def test_is_deprecated_version_eol(self):
|
|
||||||
+ '''
|
|
||||||
+ Use of is_deprecated will result to the exception,
|
|
||||||
+ if the expiration version is lower than the current version.
|
|
||||||
+ A successor function is not pointed out.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ depr = decorators.is_deprecated(self.globs, "Helium")
|
|
||||||
+ depr._curr_version = self._mk_version("Beryllium")[1]
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ depr(self.old_function)()
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The lifetime of the function "old_function" expired.'])
|
|
||||||
+
|
|
||||||
+ def test_is_deprecated_with_successor_eol(self):
|
|
||||||
+ '''
|
|
||||||
+ Use of is_deprecated will result to the exception,
|
|
||||||
+ if the expiration version is lower than the current version.
|
|
||||||
+ A successor function is pointed out.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ depr = decorators.is_deprecated(self.globs, "Helium", with_successor="new_function")
|
|
||||||
+ depr._curr_version = self._mk_version("Beryllium")[1]
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ depr(self.old_function)()
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The lifetime of the function "old_function" expired. '
|
|
||||||
+ 'Please use its successor "new_function" instead.'])
|
|
||||||
+
|
|
||||||
+ def test_is_deprecated(self):
|
|
||||||
+ '''
|
|
||||||
+ Use of is_deprecated will result to the log message,
|
|
||||||
+ if the expiration version is higher than the current version.
|
|
||||||
+ A successor function is not pointed out.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ depr = decorators.is_deprecated(self.globs, "Beryllium")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ self.assertEqual(depr(self.old_function)(), self.old_function())
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The function "old_function" is deprecated '
|
|
||||||
+ 'and will expire in version "Beryllium".'])
|
|
||||||
+
|
|
||||||
+ def test_is_deprecated_with_successor(self):
|
|
||||||
+ '''
|
|
||||||
+ Use of is_deprecated will result to the log message,
|
|
||||||
+ if the expiration version is higher than the current version.
|
|
||||||
+ A successor function is pointed out.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ depr = decorators.is_deprecated(self.globs, "Beryllium", with_successor="old_function")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ self.assertEqual(depr(self.old_function)(), self.old_function())
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The function "old_function" is deprecated '
|
|
||||||
+ 'and will expire in version "Beryllium". '
|
|
||||||
+ 'Use successor "old_function" instead.'])
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_notfound(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should raise an exception, if a same name
|
|
||||||
+ function with the "_" prefix not implemented.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['__opts__']['use_deprecated'] = ['test.new_function']
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Beryllium")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ depr(self.new_function)()
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The function is using its deprecated version and will expire in version "Beryllium". '
|
|
||||||
+ 'Use its successor "new_function" instead.'])
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_found(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should not raise an exception, if a same name
|
|
||||||
+ function with the "_" prefix is implemented, but should use
|
|
||||||
+ an old version instead, if "use_deprecated" is requested.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['__opts__']['use_deprecated'] = ['test.new_function']
|
|
||||||
+ self.globs['_new_function'] = self.old_function
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Beryllium")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ self.assertEqual(depr(self.new_function)(), self.old_function())
|
|
||||||
+ log_msg = ['The function is using its deprecated version and will expire in version "Beryllium". '
|
|
||||||
+ 'Use its successor "new_function" instead.']
|
|
||||||
+ self.assertEqual(self.messages, log_msg)
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_found_eol(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should raise an exception, if a same name
|
|
||||||
+ function with the "_" prefix is implemented, "use_deprecated" is requested
|
|
||||||
+ and EOL is reached.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['__opts__']['use_deprecated'] = ['test.new_function']
|
|
||||||
+ self.globs['_new_function'] = self.old_function
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Helium")
|
|
||||||
+ depr._curr_version = self._mk_version("Beryllium")[1]
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ depr(self.new_function)()
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['Although function "new_function" is called, an alias "new_function" '
|
|
||||||
+ 'is configured as its deprecated version. The lifetime of the function '
|
|
||||||
+ '"new_function" expired. Please use its successor "new_function" instead.'])
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_no_conf(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should not raise an exception, if a same name
|
|
||||||
+ function with the "_" prefix is implemented, but should use
|
|
||||||
+ a new version instead, if "use_deprecated" is not requested.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['_new_function'] = self.old_function
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Beryllium")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ self.assertEqual(depr(self.new_function)(), self.new_function())
|
|
||||||
+ self.assertFalse(self.messages)
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_with_name(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should not raise an exception, if a different name
|
|
||||||
+ function is implemented and specified with the "with_name" parameter,
|
|
||||||
+ but should use an old version instead and log a warning log message.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['__opts__']['use_deprecated'] = ['test.new_function']
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Beryllium", with_name="old_function")
|
|
||||||
+ depr._curr_version = self._mk_version("Helium")[1]
|
|
||||||
+ self.assertEqual(depr(self.new_function)(), self.old_function())
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['The function "old_function" is deprecated and will expire in version "Beryllium". '
|
|
||||||
+ 'Use its successor "new_function" instead.'])
|
|
||||||
+
|
|
||||||
+ def test_with_deprecated_with_name_eol(self):
|
|
||||||
+ '''
|
|
||||||
+ Test with_deprecated should raise an exception, if a different name
|
|
||||||
+ function is implemented and specified with the "with_name" parameter
|
|
||||||
+ and EOL is reached.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.globs['__opts__']['use_deprecated'] = ['test.new_function']
|
|
||||||
+ depr = decorators.with_deprecated(self.globs, "Helium", with_name="old_function")
|
|
||||||
+ depr._curr_version = self._mk_version("Beryllium")[1]
|
|
||||||
+ with self.assertRaises(CommandExecutionError):
|
|
||||||
+ depr(self.new_function)()
|
|
||||||
+ self.assertEqual(self.messages,
|
|
||||||
+ ['Although function "new_function" is called, '
|
|
||||||
+ 'an alias "old_function" is configured as its deprecated version. '
|
|
||||||
+ 'The lifetime of the function "old_function" expired. '
|
|
||||||
+ 'Please use its successor "new_function" instead.'])
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+if __name__ == '__main__':
|
|
||||||
+ from integration import run_tests
|
|
||||||
+ run_tests(DecoratorsTest, needs_daemon=False)
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
43
0008-checksum-validation-when-zypper-pkg.download.patch
Normal file
43
0008-checksum-validation-when-zypper-pkg.download.patch
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
From c80528b9aad5305b06a07cadf752a45392d4a147 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Tue, 24 May 2016 11:01:55 +0100
|
||||||
|
Subject: [PATCH 08/12] checksum validation when zypper pkg.download
|
||||||
|
|
||||||
|
check the signature of downloaded RPM files
|
||||||
|
|
||||||
|
bugfix: showing errors when a package download fails using zypper pkg.download
|
||||||
|
|
||||||
|
Renamed check_sig to checksum and some refactoring
|
||||||
|
|
||||||
|
simpler rpm.checksum function
|
||||||
|
---
|
||||||
|
salt/modules/zypper.py | 5 ++++-
|
||||||
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
|
index 92c604e..547d72b 100644
|
||||||
|
--- a/salt/modules/zypper.py
|
||||||
|
+++ b/salt/modules/zypper.py
|
||||||
|
@@ -1596,14 +1596,17 @@ def download(*packages, **kwargs):
|
||||||
|
pkg_ret = {}
|
||||||
|
for dld_result in __zypper__.xml.call('download', *packages).getElementsByTagName("download-result"):
|
||||||
|
repo = dld_result.getElementsByTagName("repository")[0]
|
||||||
|
+ path = dld_result.getElementsByTagName("localfile")[0].getAttribute("path")
|
||||||
|
pkg_info = {
|
||||||
|
'repository-name': repo.getAttribute('name'),
|
||||||
|
'repository-alias': repo.getAttribute('alias'),
|
||||||
|
+ 'path': path,
|
||||||
|
}
|
||||||
|
key = _get_first_aggregate_text(
|
||||||
|
dld_result.getElementsByTagName('name')
|
||||||
|
)
|
||||||
|
- pkg_ret[key] = pkg_info
|
||||||
|
+ if __salt__['lowpkg.checksum'](pkg_info['path']):
|
||||||
|
+ pkg_ret[key] = pkg_info
|
||||||
|
|
||||||
|
if pkg_ret:
|
||||||
|
failed = [pkg for pkg in packages if pkg not in pkg_ret]
|
||||||
|
--
|
||||||
|
2.8.2
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
From cb588505919b6c74ed824d26a184eec0f47a585b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Michael Calmer <mc@suse.de>
|
|
||||||
Date: Mon, 4 Apr 2016 09:49:31 +0200
|
|
||||||
Subject: [PATCH 09/12] fix sorting by latest version when called with an
|
|
||||||
attribute
|
|
||||||
|
|
||||||
---
|
|
||||||
salt/modules/rpm.py | 7 ++++++-
|
|
||||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py
|
|
||||||
index 6026f18..1469368 100644
|
|
||||||
--- a/salt/modules/rpm.py
|
|
||||||
+++ b/salt/modules/rpm.py
|
|
||||||
@@ -471,6 +471,7 @@ def info(*packages, **attr):
|
|
||||||
"url": "%|URL?{url: %{URL}\\n}|",
|
|
||||||
"summary": "summary: %{SUMMARY}\\n",
|
|
||||||
"description": "description:\\n%{DESCRIPTION}\\n",
|
|
||||||
+ "edition": "edition: %|EPOCH?{%{EPOCH}:}|%{VERSION}-%{RELEASE}\\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
attr = attr.get('attr', None) and attr['attr'].split(",") or None
|
|
||||||
@@ -484,6 +485,9 @@ def info(*packages, **attr):
|
|
||||||
if 'name' not in attr:
|
|
||||||
attr.append('name')
|
|
||||||
query.append(attr_map['name'])
|
|
||||||
+ if 'edition' not in attr:
|
|
||||||
+ attr.append('edition')
|
|
||||||
+ query.append(attr_map['edition'])
|
|
||||||
else:
|
|
||||||
for attr_k, attr_v in attr_map.iteritems():
|
|
||||||
if attr_k != 'description':
|
|
||||||
@@ -558,10 +562,11 @@ def info(*packages, **attr):
|
|
||||||
# pick only latest versions
|
|
||||||
# (in case multiple packages installed, e.g. kernel)
|
|
||||||
ret = dict()
|
|
||||||
- for pkg_data in reversed(sorted(_ret, cmp=lambda a_vrs, b_vrs: version_cmp(a_vrs['version'], b_vrs['version']))):
|
|
||||||
+ for pkg_data in reversed(sorted(_ret, cmp=lambda a_vrs, b_vrs: version_cmp(a_vrs['edition'], b_vrs['edition']))):
|
|
||||||
pkg_name = pkg_data.pop('name')
|
|
||||||
if pkg_name not in ret:
|
|
||||||
ret[pkg_name] = pkg_data.copy()
|
|
||||||
+ del ret[pkg_name]['edition']
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
26
0009-unit-tests-for-rpm.checksum-and-zypper.download.patch
Normal file
26
0009-unit-tests-for-rpm.checksum-and-zypper.download.patch
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
From f186137c0eb7a14697037bf46ec6a12287b9e0c9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Wed, 25 May 2016 17:08:16 +0100
|
||||||
|
Subject: [PATCH 09/12] unit tests for rpm.checksum() and zypper.download()
|
||||||
|
|
||||||
|
lint issue fixed
|
||||||
|
---
|
||||||
|
tests/unit/modules/zypper_test.py | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
||||||
|
index 308bf94..2f2b323 100644
|
||||||
|
--- a/tests/unit/modules/zypper_test.py
|
||||||
|
+++ b/tests/unit/modules/zypper_test.py
|
||||||
|
@@ -387,6 +387,7 @@ class ZypperTestCase(TestCase):
|
||||||
|
|
||||||
|
test_out = {
|
||||||
|
'nmap': {
|
||||||
|
+ 'path': u'/var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm',
|
||||||
|
'repository-alias': u'SLE-12-x86_64-Pool',
|
||||||
|
'repository-name': u'SLE-12-x86_64-Pool'
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.9.2
|
||||||
|
|
@ -1,29 +0,0 @@
|
|||||||
From 336929a4cadca55b00dbf1cd33eb35d19f420c73 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Michael Calmer <mc@suse.de>
|
|
||||||
Date: Tue, 5 Apr 2016 12:06:29 +0200
|
|
||||||
Subject: [PATCH 10/12] Prevent metadata download when getting installed
|
|
||||||
products
|
|
||||||
|
|
||||||
---
|
|
||||||
salt/modules/zypper.py | 5 ++++-
|
|
||||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
|
||||||
index 63c473c..9702f42 100644
|
|
||||||
--- a/salt/modules/zypper.py
|
|
||||||
+++ b/salt/modules/zypper.py
|
|
||||||
@@ -1309,7 +1309,10 @@ def list_products(all=False, refresh=False):
|
|
||||||
|
|
||||||
ret = list()
|
|
||||||
OEM_PATH = "/var/lib/suseRegister/OEM"
|
|
||||||
- cmd = _zypper('-x', 'products')
|
|
||||||
+ cmd = _zypper()
|
|
||||||
+ if not all:
|
|
||||||
+ cmd.append('--disable-repos')
|
|
||||||
+ cmd.extend(['-x', 'products'])
|
|
||||||
if not all:
|
|
||||||
cmd.append('-i')
|
|
||||||
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
1278
0010-snapper-execution-module.patch
Normal file
1278
0010-snapper-execution-module.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,146 +0,0 @@
|
|||||||
From aae1c09957eab3c89a6c8f78a579cdf9dcfbe188 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
|
||||||
Date: Tue, 12 Apr 2016 13:52:35 +0200
|
|
||||||
Subject: [PATCH 11/12] Check if EOL is available in a particular product
|
|
||||||
(bsc#975093)
|
|
||||||
|
|
||||||
Update SLE11 SP3 data
|
|
||||||
|
|
||||||
Update SLE12 SP1 data
|
|
||||||
|
|
||||||
Adjust test values according to the testing data
|
|
||||||
---
|
|
||||||
salt/modules/zypper.py | 13 +++++++--
|
|
||||||
.../unit/modules/zypp/zypper-products-sle11sp3.xml | 10 +++++++
|
|
||||||
.../unit/modules/zypp/zypper-products-sle12sp1.xml | 8 ++++++
|
|
||||||
tests/unit/modules/zypper_test.py | 32 ++++++++++++----------
|
|
||||||
4 files changed, 45 insertions(+), 18 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
|
||||||
index 9702f42..4ce5853 100644
|
|
||||||
--- a/salt/modules/zypper.py
|
|
||||||
+++ b/salt/modules/zypper.py
|
|
||||||
@@ -1318,12 +1318,19 @@ def list_products(all=False, refresh=False):
|
|
||||||
|
|
||||||
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
|
||||||
doc = dom.parseString(_zypper_check_result(call, xml=True))
|
|
||||||
- for prd in doc.getElementsByTagName('product-list')[0].getElementsByTagName('product'):
|
|
||||||
+ product_list = doc.getElementsByTagName('product-list')
|
|
||||||
+ if not product_list:
|
|
||||||
+ return ret # No products found
|
|
||||||
+
|
|
||||||
+ for prd in product_list[0].getElementsByTagName('product'):
|
|
||||||
p_nfo = dict()
|
|
||||||
for k_p_nfo, v_p_nfo in prd.attributes.items():
|
|
||||||
p_nfo[k_p_nfo] = k_p_nfo not in ['isbase', 'installed'] and v_p_nfo or v_p_nfo in ['true', '1']
|
|
||||||
- p_nfo['eol'] = prd.getElementsByTagName('endoflife')[0].getAttribute('text')
|
|
||||||
- p_nfo['eol_t'] = int(prd.getElementsByTagName('endoflife')[0].getAttribute('time_t'))
|
|
||||||
+
|
|
||||||
+ eol = prd.getElementsByTagName('endoflife')
|
|
||||||
+ if eol:
|
|
||||||
+ p_nfo['eol'] = eol[0].getAttribute('text')
|
|
||||||
+ p_nfo['eol_t'] = int(eol[0].getAttribute('time_t') or 0)
|
|
||||||
p_nfo['description'] = " ".join(
|
|
||||||
[line.strip() for line in _get_first_aggregate_text(
|
|
||||||
prd.getElementsByTagName('description')
|
|
||||||
diff --git a/tests/unit/modules/zypp/zypper-products-sle11sp3.xml b/tests/unit/modules/zypp/zypper-products-sle11sp3.xml
|
|
||||||
index 89a85e3..99444fe 100644
|
|
||||||
--- a/tests/unit/modules/zypp/zypper-products-sle11sp3.xml
|
|
||||||
+++ b/tests/unit/modules/zypp/zypper-products-sle11sp3.xml
|
|
||||||
@@ -31,7 +31,17 @@
|
|
||||||
offers common management tools and technology
|
|
||||||
certifications across the platform, and
|
|
||||||
each product is enterprise-class.</description></product>
|
|
||||||
+<product name="SUSE_SLES" version="11.3" release="1.201" epoch="0" arch="x86_64" productline="" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Linux Enterprise Server 11 SP3 No EOL" shortname="" flavor="" isbase="0" repo="nu_novell_com:SLES11-SP3-Updates" installed="0">0x7ffdb538e948<description>SUSE Linux Enterprise offers a comprehensive
|
|
||||||
+ suite of products built on a single code base.
|
|
||||||
+ The platform addresses business needs from
|
|
||||||
+ the smallest thin-client devices to the world’s
|
|
||||||
+ most powerful high-performance computing
|
|
||||||
+ and mainframe servers. SUSE Linux Enterprise
|
|
||||||
+ offers common management tools and technology
|
|
||||||
+ certifications across the platform, and
|
|
||||||
+ each product is enterprise-class.</description></product>
|
|
||||||
<product name="SUSE-Manager-Server" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="0" repo="nu_novell_com:SUSE-Manager-Server-2.1-Pool" installed="0"><endoflife time_t="0" text="1970-01-01T01:00:00+0100"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
|
||||||
<product name="SUSE-Manager-Server" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="manager" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="1" repo="@System" installed="1"><endoflife time_t="0" text="1970-01-01T01:00:00+0100"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
|
||||||
+<product name="SUSE-Manager-Server-Broken-EOL" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="manager" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="1" repo="@System" installed="1"><endoflife wrong="attribute"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
|
||||||
</product-list>
|
|
||||||
</stream>
|
|
||||||
diff --git a/tests/unit/modules/zypp/zypper-products-sle12sp1.xml b/tests/unit/modules/zypp/zypper-products-sle12sp1.xml
|
|
||||||
index 1a50363..a086058 100644
|
|
||||||
--- a/tests/unit/modules/zypp/zypper-products-sle12sp1.xml
|
|
||||||
+++ b/tests/unit/modules/zypp/zypper-products-sle12sp1.xml
|
|
||||||
@@ -24,6 +24,14 @@ provisioning.</description></product>
|
|
||||||
SUSE Manager Tools provide packages required to connect to a
|
|
||||||
SUSE Manager Server.
|
|
||||||
<p></description></product>
|
|
||||||
+<product name="sle-manager-tools-beta-no-eol" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><registerflavor>extension</registerflavor><description><p>
|
|
||||||
+ SUSE Manager Tools provide packages required to connect to a
|
|
||||||
+ SUSE Manager Server.
|
|
||||||
+ <p></description></product>
|
|
||||||
+<product name="sle-manager-tools-beta-broken-eol" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><endoflife wrong="attribute"/><registerflavor>extension</registerflavor><description><p>
|
|
||||||
+ SUSE Manager Tools provide packages required to connect to a
|
|
||||||
+ SUSE Manager Server.
|
|
||||||
+ <p></description></product>
|
|
||||||
<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE" summary="SUSE Linux Enterprise Server 12 SP1" repo="@System" productline="sles" registerrelease="" shortname="SLES12-SP1" flavor="DVD" isbase="true" installed="true"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
|
|
||||||
suite of products built on a single code base.
|
|
||||||
The platform addresses business needs from
|
|
||||||
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
|
||||||
index 67cf52a..97e42ef 100644
|
|
||||||
--- a/tests/unit/modules/zypper_test.py
|
|
||||||
+++ b/tests/unit/modules/zypper_test.py
|
|
||||||
@@ -153,24 +153,26 @@ class ZypperTestCase(TestCase):
|
|
||||||
for filename, test_data in {
|
|
||||||
'zypper-products-sle12sp1.xml': {
|
|
||||||
'name': ['SLES', 'SLES', 'SUSE-Manager-Proxy',
|
|
||||||
- 'SUSE-Manager-Server', 'sle-manager-tools-beta'],
|
|
||||||
+ 'SUSE-Manager-Server', 'sle-manager-tools-beta',
|
|
||||||
+ 'sle-manager-tools-beta-broken-eol', 'sle-manager-tools-beta-no-eol'],
|
|
||||||
'vendor': 'SUSE LLC <https://www.suse.com/>',
|
|
||||||
- 'release': ['0', '0', '0', '0', '0'],
|
|
||||||
- 'productline': [False, False, False, False, 'sles'],
|
|
||||||
- 'eol_t': [1509408000, 1522454400, 1522454400, 1730332800, 1730332800],
|
|
||||||
- 'isbase': [False, False, False, False, True],
|
|
||||||
- 'installed': [False, False, False, False, True],
|
|
||||||
+ 'release': ['0', '0', '0', '0', '0', '0', '0'],
|
|
||||||
+ 'productline': [False, False, False, False, False, False, 'sles'],
|
|
||||||
+ 'eol_t': [None, 0, 1509408000, 1522454400, 1522454400, 1730332800, 1730332800],
|
|
||||||
+ 'isbase': [False, False, False, False, False, False, True],
|
|
||||||
+ 'installed': [False, False, False, False, False, False, True],
|
|
||||||
},
|
|
||||||
'zypper-products-sle11sp3.xml': {
|
|
||||||
- 'name': ['SUSE-Manager-Server', 'SUSE-Manager-Server',
|
|
||||||
- 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES-SP4-migration'],
|
|
||||||
+ 'name': ['SUSE-Manager-Server', 'SUSE-Manager-Server', 'SUSE-Manager-Server-Broken-EOL',
|
|
||||||
+ 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES-SP4-migration'],
|
|
||||||
'vendor': 'SUSE LINUX Products GmbH, Nuernberg, Germany',
|
|
||||||
- 'release': ['1.138', '1.2', '1.2', '1.201', '1.4'],
|
|
||||||
- 'productline': [False, False, False, False, 'manager'],
|
|
||||||
- 'eol_t': [0, 0, 0, 0, 0],
|
|
||||||
- 'isbase': [False, False, False, False, True],
|
|
||||||
- 'installed': [False, False, False, False, True],
|
|
||||||
+ 'release': ['1.138', '1.2', '1.2', '1.2', '1.201', '1.201', '1.4'],
|
|
||||||
+ 'productline': [False, False, False, False, False, 'manager', 'manager'],
|
|
||||||
+ 'eol_t': [None, 0, 0, 0, 0, 0, 0],
|
|
||||||
+ 'isbase': [False, False, False, False, False, True, True],
|
|
||||||
+ 'installed': [False, False, False, False, False, True, True],
|
|
||||||
}}.items():
|
|
||||||
+
|
|
||||||
ref_out = {
|
|
||||||
'retcode': 0,
|
|
||||||
'stdout': get_test_data(filename)
|
|
||||||
@@ -178,10 +180,10 @@ class ZypperTestCase(TestCase):
|
|
||||||
|
|
||||||
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
|
||||||
products = zypper.list_products()
|
|
||||||
- self.assertEqual(len(products), 5)
|
|
||||||
+ self.assertEqual(len(products), 7)
|
|
||||||
self.assertIn(test_data['vendor'], [product['vendor'] for product in products])
|
|
||||||
for kwd in ['name', 'isbase', 'installed', 'release', 'productline', 'eol_t']:
|
|
||||||
- self.assertEqual(test_data[kwd], sorted([prod[kwd] for prod in products]))
|
|
||||||
+ self.assertEqual(test_data[kwd], sorted([prod.get(kwd) for prod in products]))
|
|
||||||
|
|
||||||
def test_refresh_db(self):
|
|
||||||
'''
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
|||||||
|
From e3969a80bdf2d0af0c87463af859d8daf314f018 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michael Calmer <mc@suse.de>
|
||||||
|
Date: Tue, 21 Jun 2016 13:12:48 +0200
|
||||||
|
Subject: [PATCH 11/12] 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.
|
||||||
|
---
|
||||||
|
salt/cli/salt.py | 4 +++-
|
||||||
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/cli/salt.py b/salt/cli/salt.py
|
||||||
|
index baeaf23..ec18f2e 100644
|
||||||
|
--- a/salt/cli/salt.py
|
||||||
|
+++ b/salt/cli/salt.py
|
||||||
|
@@ -264,7 +264,9 @@ class SaltCMD(parsers.SaltCMDOptionParser):
|
||||||
|
not_connected_minions = []
|
||||||
|
failed_minions = []
|
||||||
|
for each_minion in ret:
|
||||||
|
- minion_ret = ret[each_minion].get('ret')
|
||||||
|
+ minion_ret = ret[each_minion]
|
||||||
|
+ if (isinstance(minion_ret, dict) and 'ret' in minion_ret):
|
||||||
|
+ minion_ret = ret[each_minion].get('ret')
|
||||||
|
if (
|
||||||
|
isinstance(minion_ret, string_types)
|
||||||
|
and minion_ret.startswith("Minion did not return")
|
||||||
|
--
|
||||||
|
2.9.2
|
||||||
|
|
@ -1,69 +0,0 @@
|
|||||||
From 5e99ee2bec1139b1944284975454c716d477f3e0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@maryniuk.net>
|
|
||||||
Date: Wed, 13 Apr 2016 16:15:37 +0200
|
|
||||||
Subject: [PATCH 12/12] Bugfix: salt-key crashes if tries to generate keys to
|
|
||||||
the directory w/o write access (#32436)
|
|
||||||
|
|
||||||
* Raise an exception if keys are tried to be written to the directory that has no write access permissions
|
|
||||||
|
|
||||||
* Show an reasonable error message instead of a traceback crash.
|
|
||||||
|
|
||||||
* Fix the unit tests
|
|
||||||
---
|
|
||||||
salt/crypt.py | 6 ++++++
|
|
||||||
salt/scripts.py | 2 ++
|
|
||||||
tests/unit/crypt_test.py | 1 +
|
|
||||||
3 files changed, 9 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/salt/crypt.py b/salt/crypt.py
|
|
||||||
index 573a3c1..e5f3317 100644
|
|
||||||
--- a/salt/crypt.py
|
|
||||||
+++ b/salt/crypt.py
|
|
||||||
@@ -15,6 +15,7 @@ import logging
|
|
||||||
import traceback
|
|
||||||
import binascii
|
|
||||||
import weakref
|
|
||||||
+import getpass
|
|
||||||
from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin
|
|
||||||
|
|
||||||
# Import third party libs
|
|
||||||
@@ -94,6 +95,11 @@ def gen_keys(keydir, keyname, keysize, user=None):
|
|
||||||
# Between first checking and the generation another process has made
|
|
||||||
# a key! Use the winner's key
|
|
||||||
return priv
|
|
||||||
+
|
|
||||||
+ # Do not try writing anything, if directory has no permissions.
|
|
||||||
+ if not os.access(keydir, os.W_OK):
|
|
||||||
+ raise IOError('Write access denied to "{0}" for user "{1}".'.format(os.path.abspath(keydir), getpass.getuser()))
|
|
||||||
+
|
|
||||||
cumask = os.umask(191)
|
|
||||||
with salt.utils.fopen(priv, 'wb+') as f:
|
|
||||||
f.write(gen.exportKey('PEM'))
|
|
||||||
diff --git a/salt/scripts.py b/salt/scripts.py
|
|
||||||
index 7da79bf..38b100d 100644
|
|
||||||
--- a/salt/scripts.py
|
|
||||||
+++ b/salt/scripts.py
|
|
||||||
@@ -297,6 +297,8 @@ def salt_key():
|
|
||||||
SystemExit('\nExiting gracefully on Ctrl-c'),
|
|
||||||
err,
|
|
||||||
hardcrash, trace=trace)
|
|
||||||
+ except Exception as err:
|
|
||||||
+ sys.stderr.write("Error: {0}\n".format(err.message))
|
|
||||||
|
|
||||||
|
|
||||||
def salt_cp():
|
|
||||||
diff --git a/tests/unit/crypt_test.py b/tests/unit/crypt_test.py
|
|
||||||
index 3ff3b09..f548820 100644
|
|
||||||
--- a/tests/unit/crypt_test.py
|
|
||||||
+++ b/tests/unit/crypt_test.py
|
|
||||||
@@ -86,6 +86,7 @@ class CryptTestCase(TestCase):
|
|
||||||
@patch('os.umask', MagicMock())
|
|
||||||
@patch('os.chmod', MagicMock())
|
|
||||||
@patch('os.chown', MagicMock())
|
|
||||||
+ @patch('os.access', MagicMock(return_value=True))
|
|
||||||
def test_gen_keys(self):
|
|
||||||
with patch('salt.utils.fopen', mock_open()):
|
|
||||||
open_priv_wb = call('/keydir/keyname.pem', 'wb+')
|
|
||||||
--
|
|
||||||
2.1.4
|
|
||||||
|
|
24
0012-Run-salt-api-as-user-salt-bsc-990029.patch
Normal file
24
0012-Run-salt-api-as-user-salt-bsc-990029.patch
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
From af2ff55739187c59dc04e396b7787301a49a9dba Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michael Calmer <mc@suse.de>
|
||||||
|
Date: Fri, 29 Jul 2016 10:50:21 +0200
|
||||||
|
Subject: [PATCH 12/12] Run salt-api as user salt (bsc#990029)
|
||||||
|
|
||||||
|
---
|
||||||
|
pkg/salt-api.service | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/pkg/salt-api.service b/pkg/salt-api.service
|
||||||
|
index 72379ba..9be2cb8 100644
|
||||||
|
--- a/pkg/salt-api.service
|
||||||
|
+++ b/pkg/salt-api.service
|
||||||
|
@@ -3,6 +3,7 @@ Description=The Salt API
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
+User=salt
|
||||||
|
Type=simple
|
||||||
|
LimitNOFILE=8192
|
||||||
|
ExecStart=/usr/bin/salt-api
|
||||||
|
--
|
||||||
|
2.9.2
|
||||||
|
|
25
0013-Deprecate-status.uptime-one-version-later.patch
Normal file
25
0013-Deprecate-status.uptime-one-version-later.patch
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
From d19c9591c1dfbafec24a7d76402dcc9e2b17b047 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
|
Date: Thu, 4 Aug 2016 11:28:19 +0200
|
||||||
|
Subject: [PATCH 13/13] Deprecate status.uptime one version later
|
||||||
|
|
||||||
|
---
|
||||||
|
salt/modules/status.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/status.py b/salt/modules/status.py
|
||||||
|
index 029426b..cebd57b 100644
|
||||||
|
--- a/salt/modules/status.py
|
||||||
|
+++ b/salt/modules/status.py
|
||||||
|
@@ -132,7 +132,7 @@ def custom():
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
-@with_deprecated(globals(), "Boron")
|
||||||
|
+@with_deprecated(globals(), "Carbon")
|
||||||
|
def uptime():
|
||||||
|
'''
|
||||||
|
Return the uptime for this system.
|
||||||
|
--
|
||||||
|
2.9.2
|
||||||
|
|
@ -1,86 +0,0 @@
|
|||||||
From f187ee058eb221eb5a34d51ca5db53bb8eeea5e1 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@maryniuk.net>
|
|
||||||
Date: Mon, 18 Apr 2016 16:25:05 +0200
|
|
||||||
Subject: [PATCH 13/14] Prevent crash if pygit2 package is requesting
|
|
||||||
re-compilation
|
|
||||||
|
|
||||||
* Prevent crash if pygit2 package is requesting re-compilation of the entire library on production systems (no *devel packages)
|
|
||||||
|
|
||||||
* Fix PEP8: move imports to the top of the file
|
|
||||||
|
|
||||||
* Move logger up
|
|
||||||
|
|
||||||
* Add log error message in case if exception is not an ImportError
|
|
||||||
---
|
|
||||||
salt/utils/gitfs.py | 33 ++++++++++++++++++++-------------
|
|
||||||
1 file changed, 20 insertions(+), 13 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py
|
|
||||||
index 164c92e..5452c28 100644
|
|
||||||
--- a/salt/utils/gitfs.py
|
|
||||||
+++ b/salt/utils/gitfs.py
|
|
||||||
@@ -19,6 +19,18 @@ import subprocess
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
+# Import salt libs
|
|
||||||
+import salt.utils
|
|
||||||
+import salt.utils.itertools
|
|
||||||
+import salt.utils.url
|
|
||||||
+import salt.fileserver
|
|
||||||
+from salt.utils.process import os_is_running as pid_exists
|
|
||||||
+from salt.exceptions import FileserverConfigError, GitLockError
|
|
||||||
+from salt.utils.event import tagify
|
|
||||||
+
|
|
||||||
+# Import third party libs
|
|
||||||
+import salt.ext.six as six
|
|
||||||
+
|
|
||||||
VALID_PROVIDERS = ('gitpython', 'pygit2', 'dulwich')
|
|
||||||
# Optional per-remote params that can only be used on a per-remote basis, and
|
|
||||||
# thus do not have defaults in salt/config.py.
|
|
||||||
@@ -54,16 +66,8 @@ _INVALID_REPO = (
|
|
||||||
'master to continue to use this {2} remote.'
|
|
||||||
)
|
|
||||||
|
|
||||||
-# Import salt libs
|
|
||||||
-import salt.utils
|
|
||||||
-import salt.utils.itertools
|
|
||||||
-import salt.utils.url
|
|
||||||
-import salt.fileserver
|
|
||||||
-from salt.exceptions import FileserverConfigError, GitLockError
|
|
||||||
-from salt.utils.event import tagify
|
|
||||||
+log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
-# Import third party libs
|
|
||||||
-import salt.ext.six as six
|
|
||||||
# pylint: disable=import-error
|
|
||||||
try:
|
|
||||||
import git
|
|
||||||
@@ -79,8 +83,13 @@ try:
|
|
||||||
GitError = pygit2.errors.GitError
|
|
||||||
except AttributeError:
|
|
||||||
GitError = Exception
|
|
||||||
-except ImportError:
|
|
||||||
- HAS_PYGIT2 = False
|
|
||||||
+except Exception as err: # cffi VerificationError also may happen
|
|
||||||
+ HAS_PYGIT2 = False # and pygit2 requrests re-compilation
|
|
||||||
+ # on a production system (!),
|
|
||||||
+ # but cffi might be absent as well!
|
|
||||||
+ # Therefore just a generic Exception class.
|
|
||||||
+ if not isinstance(err, ImportError):
|
|
||||||
+ log.error('Import pygit2 failed: {0}'.format(err))
|
|
||||||
|
|
||||||
try:
|
|
||||||
import dulwich.errors
|
|
||||||
@@ -93,8 +102,6 @@ except ImportError:
|
|
||||||
HAS_DULWICH = False
|
|
||||||
# pylint: enable=import-error
|
|
||||||
|
|
||||||
-log = logging.getLogger(__name__)
|
|
||||||
-
|
|
||||||
# Minimum versions for backend providers
|
|
||||||
GITPYTHON_MINVER = '0.3'
|
|
||||||
PYGIT2_MINVER = '0.20.3'
|
|
||||||
--
|
|
||||||
2.8.1
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
|||||||
|
From f860f7ccb3dba6b8f0cef61e2d9658a3116e3c3c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
|
Date: Mon, 15 Aug 2016 15:03:53 +0200
|
||||||
|
Subject: [PATCH 14/15] Add ignore_repo_failure option to suppress zypper's
|
||||||
|
exit code 106 on unavailable repos
|
||||||
|
|
||||||
|
---
|
||||||
|
salt/modules/zypper.py | 23 ++++++++++++++++++++---
|
||||||
|
1 file changed, 20 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
|
index 547d72b..1c60f0f 100644
|
||||||
|
--- a/salt/modules/zypper.py
|
||||||
|
+++ b/salt/modules/zypper.py
|
||||||
|
@@ -100,6 +100,18 @@ class _Zypper(object):
|
||||||
|
self.__no_lock = False
|
||||||
|
self.__no_raise = False
|
||||||
|
self.__refresh = False
|
||||||
|
+ self.__ignore_repo_failure = False
|
||||||
|
+
|
||||||
|
+ def __call__(self, *args, **kwargs):
|
||||||
|
+ '''
|
||||||
|
+ :param args:
|
||||||
|
+ :param kwargs:
|
||||||
|
+ :return:
|
||||||
|
+ '''
|
||||||
|
+ # Ignore exit code for 106 (repo is not available)
|
||||||
|
+ if 'no_repo_failure' in kwargs:
|
||||||
|
+ self.__ignore_repo_failure = kwargs['no_repo_failure']
|
||||||
|
+ return self
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
'''
|
||||||
|
@@ -275,7 +287,7 @@ class _Zypper(object):
|
||||||
|
__salt__['event.fire_master']({'success': not len(self.error_msg),
|
||||||
|
'info': self.error_msg or 'Zypper has been released'},
|
||||||
|
self.TAG_RELEASED)
|
||||||
|
- if self.error_msg and not self.__no_raise:
|
||||||
|
+ if self.error_msg and not self.__no_raise and not self.__ignore_repo_failure:
|
||||||
|
raise CommandExecutionError('Zypper command failure: {0}'.format(self.error_msg))
|
||||||
|
|
||||||
|
return self._is_xml_mode() and dom.parseString(self.__call_result['stdout']) or self.__call_result['stdout']
|
||||||
|
@@ -863,6 +875,7 @@ def install(name=None,
|
||||||
|
downloadonly=None,
|
||||||
|
skip_verify=False,
|
||||||
|
version=None,
|
||||||
|
+ ignore_repo_failure=False,
|
||||||
|
**kwargs):
|
||||||
|
'''
|
||||||
|
Install the passed package(s), add refresh=True to force a 'zypper refresh'
|
||||||
|
@@ -929,6 +942,10 @@ def install(name=None,
|
||||||
|
|
||||||
|
salt '*' pkg.install sources='[{"foo": "salt://foo.rpm"},{"bar": "salt://bar.rpm"}]'
|
||||||
|
|
||||||
|
+ ignore_repo_failure
|
||||||
|
+ Zypper returns error code 106 if one of the repositories are not available for various reasons.
|
||||||
|
+ In case to set strict check, this parameter needs to be set to True. Default: False.
|
||||||
|
+
|
||||||
|
|
||||||
|
Returns a dict containing the new package names and versions::
|
||||||
|
|
||||||
|
@@ -1000,7 +1017,7 @@ def install(name=None,
|
||||||
|
while targets:
|
||||||
|
cmd = cmd_install + targets[:500]
|
||||||
|
targets = targets[500:]
|
||||||
|
- for line in __zypper__.call(*cmd).splitlines():
|
||||||
|
+ for line in __zypper__(no_repo_failure=ignore_repo_failure).call(*cmd).splitlines():
|
||||||
|
match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
|
||||||
|
if match:
|
||||||
|
downgrades.append(match.group(1))
|
||||||
|
@@ -1008,7 +1025,7 @@ def install(name=None,
|
||||||
|
while downgrades:
|
||||||
|
cmd = cmd_install + ['--force'] + downgrades[:500]
|
||||||
|
downgrades = downgrades[500:]
|
||||||
|
- __zypper__.call(*cmd)
|
||||||
|
+ __zypper__(no_repo_failure=ignore_repo_failure).call(*cmd)
|
||||||
|
|
||||||
|
__context__.pop('pkg.list_pkgs', None)
|
||||||
|
new = list_pkgs()
|
||||||
|
--
|
||||||
|
2.9.3
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
From 0961f5bd3e3b7aa3ebd75fe064044d078df62724 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Michael Calmer <mc@suse.de>
|
|
||||||
Date: Mon, 18 Apr 2016 16:31:58 +0200
|
|
||||||
Subject: [PATCH 14/14] align OS grains from older SLES with current one
|
|
||||||
(#32649)
|
|
||||||
|
|
||||||
---
|
|
||||||
salt/grains/core.py | 9 +++++++--
|
|
||||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/grains/core.py b/salt/grains/core.py
|
|
||||||
index eb62b97..d5dbef8 100644
|
|
||||||
--- a/salt/grains/core.py
|
|
||||||
+++ b/salt/grains/core.py
|
|
||||||
@@ -1184,14 +1184,19 @@ def os_data():
|
|
||||||
for line in fhr:
|
|
||||||
if 'enterprise' in line.lower():
|
|
||||||
grains['lsb_distrib_id'] = 'SLES'
|
|
||||||
+ grains['lsb_distrib_codename'] = re.sub(r'\(.+\)', '', line).strip()
|
|
||||||
elif 'version' in line.lower():
|
|
||||||
version = re.sub(r'[^0-9]', '', line)
|
|
||||||
elif 'patchlevel' in line.lower():
|
|
||||||
patch = re.sub(r'[^0-9]', '', line)
|
|
||||||
grains['lsb_distrib_release'] = version
|
|
||||||
if patch:
|
|
||||||
- grains['lsb_distrib_release'] += ' SP' + patch
|
|
||||||
- grains['lsb_distrib_codename'] = 'n.a'
|
|
||||||
+ grains['lsb_distrib_release'] += '.' + patch
|
|
||||||
+ patchstr = 'SP' + patch
|
|
||||||
+ if grains['lsb_distrib_codename'] and patchstr not in grains['lsb_distrib_codename']:
|
|
||||||
+ grains['lsb_distrib_codename'] += ' ' + patchstr
|
|
||||||
+ if not grains['lsb_distrib_codename']:
|
|
||||||
+ grains['lsb_distrib_codename'] = 'n.a'
|
|
||||||
elif os.path.isfile('/etc/altlinux-release'):
|
|
||||||
# ALT Linux
|
|
||||||
grains['lsb_distrib_id'] = 'altlinux'
|
|
||||||
--
|
|
||||||
2.8.1
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
|||||||
|
From 0809c60c693eb5d2e9569c24d995818097c6920d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
|
Date: Mon, 15 Aug 2016 17:35:01 +0200
|
||||||
|
Subject: [PATCH 15/15] Remove zypper's raise exception if mod_repo has no
|
||||||
|
arguments and/or no changes
|
||||||
|
|
||||||
|
* Fix Unit test for suppressing the exception removal on non-modified repos
|
||||||
|
---
|
||||||
|
salt/modules/zypper.py | 11 +++++++----
|
||||||
|
tests/unit/modules/zypper_test.py | 15 +++------------
|
||||||
|
2 files changed, 10 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
|
index 1c60f0f..1ec903e 100644
|
||||||
|
--- a/salt/modules/zypper.py
|
||||||
|
+++ b/salt/modules/zypper.py
|
||||||
|
@@ -824,6 +824,7 @@ def mod_repo(repo, **kwargs):
|
||||||
|
cmd_opt = global_cmd_opt + ['mr'] + cmd_opt + [repo]
|
||||||
|
__zypper__.refreshable.xml.call(*cmd_opt)
|
||||||
|
|
||||||
|
+ comment = None
|
||||||
|
if call_refresh:
|
||||||
|
# when used with "zypper ar --refresh" or "zypper mr --refresh"
|
||||||
|
# --gpg-auto-import-keys is not doing anything
|
||||||
|
@@ -831,11 +832,13 @@ def mod_repo(repo, **kwargs):
|
||||||
|
refresh_opts = global_cmd_opt + ['refresh'] + [repo]
|
||||||
|
__zypper__.xml.call(*refresh_opts)
|
||||||
|
elif not added and not cmd_opt:
|
||||||
|
- raise CommandExecutionError(
|
||||||
|
- 'Specified arguments did not result in modification of repo'
|
||||||
|
- )
|
||||||
|
+ comment = 'Specified arguments did not result in modification of repo'
|
||||||
|
+
|
||||||
|
+ repo = get_repo(repo)
|
||||||
|
+ if comment:
|
||||||
|
+ repo['comment'] = comment
|
||||||
|
|
||||||
|
- return get_repo(repo)
|
||||||
|
+ return repo
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_db():
|
||||||
|
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
||||||
|
index 2f2b323..7caa707 100644
|
||||||
|
--- a/tests/unit/modules/zypper_test.py
|
||||||
|
+++ b/tests/unit/modules/zypper_test.py
|
||||||
|
@@ -73,7 +73,7 @@ class ZypperTestCase(TestCase):
|
||||||
|
self.zypper_patcher_config = {
|
||||||
|
'_get_configured_repos': Mock(side_effect=side_effect),
|
||||||
|
'__zypper__': Mock(),
|
||||||
|
- 'get_repo': Mock()
|
||||||
|
+ 'get_repo': Mock(return_value={})
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_list_upgrades(self):
|
||||||
|
@@ -493,17 +493,8 @@ class ZypperTestCase(TestCase):
|
||||||
|
'salt.modules.zypper', **self.zypper_patcher_config)
|
||||||
|
|
||||||
|
with zypper_patcher:
|
||||||
|
- with self.assertRaisesRegexp(
|
||||||
|
- Exception,
|
||||||
|
- 'Specified arguments did not result in modification of repo'
|
||||||
|
- ):
|
||||||
|
- zypper.mod_repo(name, **{'url': url})
|
||||||
|
- with self.assertRaisesRegexp(
|
||||||
|
- Exception,
|
||||||
|
- 'Specified arguments did not result in modification of repo'
|
||||||
|
- ):
|
||||||
|
- zypper.mod_repo(name, **{'url': url, 'gpgautoimport': 'a'})
|
||||||
|
-
|
||||||
|
+ self.assertEqual(zypper.mod_repo(name, **{'url': url}),
|
||||||
|
+ {'comment': 'Specified arguments did not result in modification of repo'})
|
||||||
|
zypper.__zypper__.xml.call.assert_not_called()
|
||||||
|
zypper.__zypper__.refreshable.xml.call.assert_not_called()
|
||||||
|
|
||||||
|
--
|
||||||
|
2.9.3
|
||||||
|
|
@ -1,904 +0,0 @@
|
|||||||
From e52c7926a699bdee3fad2767c8aa755ee115c5d7 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
|
||||||
Date: Fri, 22 Apr 2016 14:59:14 +0200
|
|
||||||
Subject: [PATCH 15/15] Unblock Zypper. Modify environment.
|
|
||||||
|
|
||||||
* Bugfix: version_cmp crashes in CLI if there are versions, that looks like integer or float.
|
|
||||||
|
|
||||||
* Standarize zypper call to "run_all"
|
|
||||||
|
|
||||||
* Remove verbose wrapping
|
|
||||||
|
|
||||||
* Remove an empty line
|
|
||||||
|
|
||||||
* Remove an unused variable
|
|
||||||
|
|
||||||
* Remove one-char variables
|
|
||||||
|
|
||||||
* Implement block-proof Zypper call implementation
|
|
||||||
|
|
||||||
* Remove blocking-prone Zypper call implementation
|
|
||||||
|
|
||||||
* Use new Zypper call implementation
|
|
||||||
|
|
||||||
* Fire an event to the Master about blocked Zypper.
|
|
||||||
|
|
||||||
* Add Zypper lock constant
|
|
||||||
|
|
||||||
* Check if zypper lock exists and add more debug logging
|
|
||||||
|
|
||||||
* Replace string values with the constants
|
|
||||||
|
|
||||||
* Fire an event about released Zypper with its result
|
|
||||||
|
|
||||||
* Bugfix: accept refresh override param
|
|
||||||
|
|
||||||
* Update docstrings according to the bugfix
|
|
||||||
|
|
||||||
* Make Zypper caller module-level reusable
|
|
||||||
|
|
||||||
* Bugfix: inverted logic on raising (or not) exceptions
|
|
||||||
|
|
||||||
* Add Zypper Call mock
|
|
||||||
|
|
||||||
* Remove an obsolete test case
|
|
||||||
|
|
||||||
* Fix tests according to the new calling model
|
|
||||||
|
|
||||||
* Bugfix: always trigger __getattr__ to reset and increment the configuration before the call.
|
|
||||||
|
|
||||||
* Add Zypper caller test suite
|
|
||||||
|
|
||||||
* Parse DOM out of the box, when XML mode is called
|
|
||||||
|
|
||||||
* Add exception handling test
|
|
||||||
|
|
||||||
* Test DOM parsing
|
|
||||||
|
|
||||||
* Rename tags
|
|
||||||
|
|
||||||
* Fix PID file path for SLE11
|
|
||||||
|
|
||||||
* Move log message down to the point where it actually sleeps. Rephrase the message.
|
|
||||||
|
|
||||||
* Remove unused variable in a constructor. Adjust the docstring accordingly.
|
|
||||||
|
|
||||||
* Prevent the use of "refreshable" together with "nolock" option.
|
|
||||||
---
|
|
||||||
salt/modules/zypper.py | 403 +++++++++++++++++++++++++-------------
|
|
||||||
tests/unit/modules/zypper_test.py | 133 +++++++------
|
|
||||||
2 files changed, 345 insertions(+), 191 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
|
||||||
index 4ce5853..53b5d9f 100644
|
|
||||||
--- a/salt/modules/zypper.py
|
|
||||||
+++ b/salt/modules/zypper.py
|
|
||||||
@@ -11,10 +11,13 @@ import copy
|
|
||||||
import logging
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
+import time
|
|
||||||
+import datetime
|
|
||||||
|
|
||||||
# Import 3rd-party libs
|
|
||||||
# pylint: disable=import-error,redefined-builtin,no-name-in-module
|
|
||||||
import salt.ext.six as six
|
|
||||||
+import salt.utils.event
|
|
||||||
from salt.ext.six.moves import configparser
|
|
||||||
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse
|
|
||||||
# pylint: enable=import-error,redefined-builtin,no-name-in-module
|
|
||||||
@@ -51,65 +54,226 @@ def __virtual__():
|
|
||||||
return __virtualname__
|
|
||||||
|
|
||||||
|
|
||||||
-def _zypper(*opts):
|
|
||||||
- '''
|
|
||||||
- Return zypper command with default options as a list.
|
|
||||||
-
|
|
||||||
- opts
|
|
||||||
- additional options for zypper command
|
|
||||||
-
|
|
||||||
- '''
|
|
||||||
- cmd = ['zypper', '--non-interactive']
|
|
||||||
- cmd.extend(opts)
|
|
||||||
-
|
|
||||||
- return cmd
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-def _is_zypper_error(retcode):
|
|
||||||
- '''
|
|
||||||
- Return True in case the exist code indicate a zypper errror.
|
|
||||||
- Otherwise False
|
|
||||||
- '''
|
|
||||||
- # see man zypper for existing exit codes
|
|
||||||
- return int(retcode) not in [0, 100, 101, 102, 103]
|
|
||||||
+class _Zypper(object):
|
|
||||||
+ '''
|
|
||||||
+ Zypper parallel caller.
|
|
||||||
+ Validates the result and either raises an exception or reports an error.
|
|
||||||
+ Allows serial zypper calls (first came, first won).
|
|
||||||
+ '''
|
|
||||||
+
|
|
||||||
+ SUCCESS_EXIT_CODES = [0, 100, 101, 102, 103]
|
|
||||||
+ LOCK_EXIT_CODE = 7
|
|
||||||
+ XML_DIRECTIVES = ['-x', '--xmlout']
|
|
||||||
+ ZYPPER_LOCK = '/var/run/zypp.pid'
|
|
||||||
+ TAG_RELEASED = 'zypper/released'
|
|
||||||
+ TAG_BLOCKED = 'zypper/blocked'
|
|
||||||
+
|
|
||||||
+ def __init__(self):
|
|
||||||
+ '''
|
|
||||||
+ Constructor
|
|
||||||
+ '''
|
|
||||||
+ self.__called = False
|
|
||||||
+ self._reset()
|
|
||||||
+
|
|
||||||
+ def _reset(self):
|
|
||||||
+ '''
|
|
||||||
+ Resets values of the call setup.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.__cmd = ['zypper', '--non-interactive']
|
|
||||||
+ self.__exit_code = 0
|
|
||||||
+ self.__call_result = dict()
|
|
||||||
+ self.__error_msg = ''
|
|
||||||
+ self.__env = {'SALT_RUNNING': "1"} # Subject to change
|
|
||||||
+
|
|
||||||
+ # Call config
|
|
||||||
+ self.__xml = False
|
|
||||||
+ self.__no_lock = False
|
|
||||||
+ self.__no_raise = False
|
|
||||||
+ self.__refresh = False
|
|
||||||
+
|
|
||||||
+ def __getattr__(self, item):
|
|
||||||
+ '''
|
|
||||||
+ Call configurator.
|
|
||||||
+
|
|
||||||
+ :param item:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ # Reset after the call
|
|
||||||
+ if self.__called:
|
|
||||||
+ self._reset()
|
|
||||||
+ self.__called = False
|
|
||||||
+
|
|
||||||
+ if item == 'xml':
|
|
||||||
+ self.__xml = True
|
|
||||||
+ elif item == 'nolock':
|
|
||||||
+ self.__no_lock = True
|
|
||||||
+ elif item == 'noraise':
|
|
||||||
+ self.__no_raise = True
|
|
||||||
+ elif item == 'refreshable':
|
|
||||||
+ self.__refresh = True
|
|
||||||
+ elif item == 'call':
|
|
||||||
+ return self.__call
|
|
||||||
+ else:
|
|
||||||
+ return self.__dict__[item]
|
|
||||||
+
|
|
||||||
+ # Prevent the use of "refreshable" together with "nolock".
|
|
||||||
+ if self.__no_lock:
|
|
||||||
+ self.__no_lock = not self.__refresh
|
|
||||||
+
|
|
||||||
+ return self
|
|
||||||
+
|
|
||||||
+ @property
|
|
||||||
+ def exit_code(self):
|
|
||||||
+ return self.__exit_code
|
|
||||||
+
|
|
||||||
+ @exit_code.setter
|
|
||||||
+ def exit_code(self, exit_code):
|
|
||||||
+ self.__exit_code = int(exit_code or '0')
|
|
||||||
+
|
|
||||||
+ @property
|
|
||||||
+ def error_msg(self):
|
|
||||||
+ return self.__error_msg
|
|
||||||
+
|
|
||||||
+ @error_msg.setter
|
|
||||||
+ def error_msg(self, msg):
|
|
||||||
+ if self._is_error():
|
|
||||||
+ self.__error_msg = msg and os.linesep.join(msg) or "Check Zypper's logs."
|
|
||||||
+
|
|
||||||
+ def stdout(self):
|
|
||||||
+ return self.__call_result.get('stdout', '')
|
|
||||||
+
|
|
||||||
+ def stderr(self):
|
|
||||||
+ return self.__call_result.get('stderr', '')
|
|
||||||
+
|
|
||||||
+ def _is_error(self):
|
|
||||||
+ '''
|
|
||||||
+ Is this is an error code?
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ return self.exit_code not in self.SUCCESS_EXIT_CODES
|
|
||||||
+
|
|
||||||
+ def _is_lock(self):
|
|
||||||
+ '''
|
|
||||||
+ Is this is a lock error code?
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ return self.exit_code == self.LOCK_EXIT_CODE
|
|
||||||
+
|
|
||||||
+ def _is_xml_mode(self):
|
|
||||||
+ '''
|
|
||||||
+ Is Zypper's output is in XML format?
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ return [itm for itm in self.XML_DIRECTIVES if itm in self.__cmd] and True or False
|
|
||||||
+
|
|
||||||
+ def _check_result(self):
|
|
||||||
+ '''
|
|
||||||
+ Check and set the result of a zypper command. In case of an error,
|
|
||||||
+ either raise a CommandExecutionError or extract the error.
|
|
||||||
+
|
|
||||||
+ result
|
|
||||||
+ The result of a zypper command called with cmd.run_all
|
|
||||||
+ '''
|
|
||||||
+ if not self.__call_result:
|
|
||||||
+ raise CommandExecutionError('No output result from Zypper?')
|
|
||||||
+
|
|
||||||
+ self.exit_code = self.__call_result['retcode']
|
|
||||||
+ if self._is_lock():
|
|
||||||
+ return False
|
|
||||||
+
|
|
||||||
+ if self._is_error():
|
|
||||||
+ _error_msg = list()
|
|
||||||
+ if not self._is_xml_mode():
|
|
||||||
+ msg = self.__call_result['stderr'] and self.__call_result['stderr'].strip() or ""
|
|
||||||
+ if msg:
|
|
||||||
+ _error_msg.append(msg)
|
|
||||||
+ else:
|
|
||||||
+ try:
|
|
||||||
+ doc = dom.parseString(self.__call_result['stdout'])
|
|
||||||
+ except ExpatError as err:
|
|
||||||
+ log.error(err)
|
|
||||||
+ doc = None
|
|
||||||
+ if doc:
|
|
||||||
+ msg_nodes = doc.getElementsByTagName('message')
|
|
||||||
+ for node in msg_nodes:
|
|
||||||
+ if node.getAttribute('type') == 'error':
|
|
||||||
+ _error_msg.append(node.childNodes[0].nodeValue)
|
|
||||||
+ elif self.__call_result['stderr'].strip():
|
|
||||||
+ _error_msg.append(self.__call_result['stderr'].strip())
|
|
||||||
+ self.error_msg = _error_msg
|
|
||||||
+ return True
|
|
||||||
+
|
|
||||||
+ def __call(self, *args, **kwargs):
|
|
||||||
+ '''
|
|
||||||
+ Call Zypper.
|
|
||||||
+
|
|
||||||
+ :param state:
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ self.__called = True
|
|
||||||
+ if self.__xml:
|
|
||||||
+ self.__cmd.append('--xmlout')
|
|
||||||
+ if not self.__refresh:
|
|
||||||
+ self.__cmd.append('--no-refresh')
|
|
||||||
+
|
|
||||||
+ self.__cmd.extend(args)
|
|
||||||
+ kwargs['output_loglevel'] = 'trace'
|
|
||||||
+ kwargs['python_shell'] = False
|
|
||||||
+ kwargs['env'] = self.__env.copy()
|
|
||||||
+ if self.__no_lock:
|
|
||||||
+ kwargs['env']['ZYPP_READONLY_HACK'] = "1" # Disables locking for read-only operations. Do not try that at home!
|
|
||||||
+
|
|
||||||
+ # Zypper call will stuck here waiting, if another zypper hangs until forever.
|
|
||||||
+ # However, Zypper lock needs to be always respected.
|
|
||||||
+ was_blocked = False
|
|
||||||
+ while True:
|
|
||||||
+ log.debug("Calling Zypper: " + ' '.join(self.__cmd))
|
|
||||||
+ self.__call_result = __salt__['cmd.run_all'](self.__cmd, **kwargs)
|
|
||||||
+ if self._check_result():
|
|
||||||
+ break
|
|
||||||
+
|
|
||||||
+ if os.path.exists(self.ZYPPER_LOCK):
|
|
||||||
+ try:
|
|
||||||
+ data = __salt__['ps.proc_info'](int(open(self.ZYPPER_LOCK).readline()),
|
|
||||||
+ attrs=['pid', 'name', 'cmdline', 'create_time'])
|
|
||||||
+ data['cmdline'] = ' '.join(data['cmdline'])
|
|
||||||
+ data['info'] = 'Blocking process created at {0}.'.format(
|
|
||||||
+ datetime.datetime.utcfromtimestamp(data['create_time']).isoformat())
|
|
||||||
+ data['success'] = True
|
|
||||||
+ except Exception as err:
|
|
||||||
+ data = {'info': 'Unable to retrieve information about blocking process: {0}'.format(err.message),
|
|
||||||
+ 'success': False}
|
|
||||||
+ else:
|
|
||||||
+ data = {'info': 'Zypper is locked, but no Zypper lock has been found.', 'success': False}
|
|
||||||
|
|
||||||
+ if not data['success']:
|
|
||||||
+ log.debug("Unable to collect data about blocking process.")
|
|
||||||
+ else:
|
|
||||||
+ log.debug("Collected data about blocking process.")
|
|
||||||
|
|
||||||
-def _zypper_check_result(result, xml=False):
|
|
||||||
- '''
|
|
||||||
- Check the result of a zypper command. In case of an error, it raise
|
|
||||||
- a CommandExecutionError. Otherwise it returns stdout string of the
|
|
||||||
- command.
|
|
||||||
+ __salt__['event.fire_master'](data, self.TAG_BLOCKED)
|
|
||||||
+ log.debug("Fired a Zypper blocked event to the master with the data: {0}".format(str(data)))
|
|
||||||
+ log.debug("Waiting 5 seconds for Zypper gets released...")
|
|
||||||
+ time.sleep(5)
|
|
||||||
+ if not was_blocked:
|
|
||||||
+ was_blocked = True
|
|
||||||
|
|
||||||
- result
|
|
||||||
- The result of a zypper command called with cmd.run_all
|
|
||||||
+ if was_blocked:
|
|
||||||
+ __salt__['event.fire_master']({'success': not len(self.error_msg),
|
|
||||||
+ 'info': self.error_msg or 'Zypper has been released'},
|
|
||||||
+ self.TAG_RELEASED)
|
|
||||||
+ if self.error_msg and not self.__no_raise:
|
|
||||||
+ raise CommandExecutionError('Zypper command failure: {0}'.format(self.error_msg))
|
|
||||||
|
|
||||||
- xml
|
|
||||||
- Set to True if zypper command was called with --xmlout.
|
|
||||||
- In this case it try to read an error message out of the XML
|
|
||||||
- stream. Default is False.
|
|
||||||
- '''
|
|
||||||
- if _is_zypper_error(result['retcode']):
|
|
||||||
- msg = list()
|
|
||||||
- if not xml:
|
|
||||||
- msg.append(result['stderr'] and result['stderr'] or "")
|
|
||||||
- else:
|
|
||||||
- try:
|
|
||||||
- doc = dom.parseString(result['stdout'])
|
|
||||||
- except ExpatError as err:
|
|
||||||
- log.error(err)
|
|
||||||
- doc = None
|
|
||||||
- if doc:
|
|
||||||
- msg_nodes = doc.getElementsByTagName('message')
|
|
||||||
- for node in msg_nodes:
|
|
||||||
- if node.getAttribute('type') == 'error':
|
|
||||||
- msg.append(node.childNodes[0].nodeValue)
|
|
||||||
- elif result['stderr'].strip():
|
|
||||||
- msg.append(result['stderr'].strip())
|
|
||||||
+ return self._is_xml_mode() and dom.parseString(self.__call_result['stdout']) or self.__call_result['stdout']
|
|
||||||
|
|
||||||
- raise CommandExecutionError("zypper command failed: {0}".format(
|
|
||||||
- msg and os.linesep.join(msg) or "Check zypper logs"))
|
|
||||||
|
|
||||||
- return result['stdout']
|
|
||||||
+__zypper__ = _Zypper()
|
|
||||||
|
|
||||||
|
|
||||||
def list_upgrades(refresh=True):
|
|
||||||
@@ -129,10 +293,9 @@ def list_upgrades(refresh=True):
|
|
||||||
'''
|
|
||||||
if refresh:
|
|
||||||
refresh_db()
|
|
||||||
+
|
|
||||||
ret = dict()
|
|
||||||
- run_data = __salt__['cmd.run_all'](_zypper('-x', 'list-updates'), output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(run_data, xml=True))
|
|
||||||
- for update_node in doc.getElementsByTagName('update'):
|
|
||||||
+ for update_node in __zypper__.nolock.xml.call('list-updates').getElementsByTagName('update'):
|
|
||||||
if update_node.getAttribute('kind') == 'package':
|
|
||||||
ret[update_node.getAttribute('name')] = update_node.getAttribute('edition')
|
|
||||||
|
|
||||||
@@ -191,7 +354,6 @@ def info_installed(*names, **kwargs):
|
|
||||||
t_nfo['source'] = value
|
|
||||||
else:
|
|
||||||
t_nfo[key] = value
|
|
||||||
-
|
|
||||||
ret[pkg_name] = t_nfo
|
|
||||||
|
|
||||||
return ret
|
|
||||||
@@ -230,8 +392,8 @@ def info_available(*names, **kwargs):
|
|
||||||
|
|
||||||
# Run in batches
|
|
||||||
while batch:
|
|
||||||
- cmd = _zypper('info', '-t', 'package', *batch[:batch_size])
|
|
||||||
- pkg_info.extend(re.split(r"Information for package*", __salt__['cmd.run_stdout'](cmd, output_loglevel='trace')))
|
|
||||||
+ pkg_info.extend(re.split(r"Information for package*",
|
|
||||||
+ __zypper__.nolock.call('info', '-t', 'package', *batch[:batch_size])))
|
|
||||||
batch = batch[batch_size:]
|
|
||||||
|
|
||||||
for pkg_data in pkg_info:
|
|
||||||
@@ -280,6 +442,11 @@ def latest_version(*names, **kwargs):
|
|
||||||
If the latest version of a given package is already installed, an empty
|
|
||||||
dict will be returned for that package.
|
|
||||||
|
|
||||||
+ refresh
|
|
||||||
+ force a refresh if set to True (default).
|
|
||||||
+ If set to False it depends on zypper if a refresh is
|
|
||||||
+ executed or not.
|
|
||||||
+
|
|
||||||
CLI example:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
@@ -293,7 +460,7 @@ def latest_version(*names, **kwargs):
|
|
||||||
return ret
|
|
||||||
|
|
||||||
names = sorted(list(set(names)))
|
|
||||||
- package_info = info_available(*names)
|
|
||||||
+ package_info = info_available(*names, **kwargs)
|
|
||||||
for name in names:
|
|
||||||
pkg_info = package_info.get(name, {})
|
|
||||||
status = pkg_info.get('status', '').lower()
|
|
||||||
@@ -311,10 +478,15 @@ def latest_version(*names, **kwargs):
|
|
||||||
available_version = salt.utils.alias_function(latest_version, 'available_version')
|
|
||||||
|
|
||||||
|
|
||||||
-def upgrade_available(name):
|
|
||||||
+def upgrade_available(name, **kwargs):
|
|
||||||
'''
|
|
||||||
Check whether or not an upgrade is available for a given package
|
|
||||||
|
|
||||||
+ refresh
|
|
||||||
+ force a refresh if set to True (default).
|
|
||||||
+ If set to False it depends on zypper if a refresh is
|
|
||||||
+ executed or not.
|
|
||||||
+
|
|
||||||
CLI Example:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
@@ -322,7 +494,7 @@ def upgrade_available(name):
|
|
||||||
salt '*' pkg.upgrade_available <package name>
|
|
||||||
'''
|
|
||||||
# The "not not" tactic is intended here as it forces the return to be False.
|
|
||||||
- return not not latest_version(name) # pylint: disable=C0113
|
|
||||||
+ return not not latest_version(name, **kwargs) # pylint: disable=C0113
|
|
||||||
|
|
||||||
|
|
||||||
def version(*names, **kwargs):
|
|
||||||
@@ -355,7 +527,7 @@ def version_cmp(ver1, ver2):
|
|
||||||
|
|
||||||
salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002'
|
|
||||||
'''
|
|
||||||
- return __salt__['lowpkg.version_cmp'](ver1, ver2)
|
|
||||||
+ return __salt__['lowpkg.version_cmp'](str(ver1), str(ver2))
|
|
||||||
|
|
||||||
|
|
||||||
def list_pkgs(versions_as_list=False, **kwargs):
|
|
||||||
@@ -398,12 +570,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
|
|
||||||
|
|
||||||
cmd = ['rpm', '-qa', '--queryformat', '%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%|EPOCH?{%{EPOCH}}:{}|\\n']
|
|
||||||
ret = {}
|
|
||||||
- out = __salt__['cmd.run'](
|
|
||||||
- cmd,
|
|
||||||
- output_loglevel='trace',
|
|
||||||
- python_shell=False
|
|
||||||
- )
|
|
||||||
- for line in out.splitlines():
|
|
||||||
+ for line in __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False).splitlines():
|
|
||||||
name, pkgver, rel, epoch = line.split('_|-')
|
|
||||||
if epoch:
|
|
||||||
pkgver = '{0}:{1}'.format(epoch, pkgver)
|
|
||||||
@@ -415,6 +582,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
|
|
||||||
__context__['pkg.list_pkgs'] = copy.deepcopy(ret)
|
|
||||||
if not versions_as_list:
|
|
||||||
__salt__['pkg_resource.stringify'](ret)
|
|
||||||
+
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
@@ -434,15 +602,13 @@ def _get_repo_info(alias, repos_cfg=None):
|
|
||||||
Get one repo meta-data.
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
- meta = dict((repos_cfg or _get_configured_repos()).items(alias))
|
|
||||||
- meta['alias'] = alias
|
|
||||||
- for key, val in six.iteritems(meta):
|
|
||||||
- if val in ['0', '1']:
|
|
||||||
- meta[key] = int(meta[key]) == 1
|
|
||||||
- elif val == 'NONE':
|
|
||||||
- meta[key] = None
|
|
||||||
- return meta
|
|
||||||
- except (ValueError, configparser.NoSectionError) as error:
|
|
||||||
+ ret = dict((repos_cfg or _get_configured_repos()).items(alias))
|
|
||||||
+ ret['alias'] = alias
|
|
||||||
+ for key, val in six.iteritems(ret):
|
|
||||||
+ if val == 'NONE':
|
|
||||||
+ ret[key] = None
|
|
||||||
+ return ret
|
|
||||||
+ except (ValueError, configparser.NoSectionError):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -490,9 +656,7 @@ def del_repo(repo):
|
|
||||||
repos_cfg = _get_configured_repos()
|
|
||||||
for alias in repos_cfg.sections():
|
|
||||||
if alias == repo:
|
|
||||||
- cmd = _zypper('-x', 'rr', '--loose-auth', '--loose-query', alias)
|
|
||||||
- ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(ret, xml=True))
|
|
||||||
+ doc = __zypper__.xml.call('rr', '--loose-auth', '--loose-query', alias)
|
|
||||||
msg = doc.getElementsByTagName('message')
|
|
||||||
if doc.getElementsByTagName('progress') and msg:
|
|
||||||
return {
|
|
||||||
@@ -576,8 +740,7 @@ def mod_repo(repo, **kwargs):
|
|
||||||
'Repository \'{0}\' already exists as \'{1}\'.'.format(repo, alias))
|
|
||||||
|
|
||||||
# Add new repo
|
|
||||||
- _zypper_check_result(__salt__['cmd.run_all'](_zypper('-x', 'ar', url, repo),
|
|
||||||
- output_loglevel='trace'), xml=True)
|
|
||||||
+ __zypper__.xml.call('ar', url, repo)
|
|
||||||
|
|
||||||
# Verify the repository has been added
|
|
||||||
repos_cfg = _get_configured_repos()
|
|
||||||
@@ -613,9 +776,7 @@ def mod_repo(repo, **kwargs):
|
|
||||||
|
|
||||||
if cmd_opt:
|
|
||||||
cmd_opt.append(repo)
|
|
||||||
- ret = __salt__['cmd.run_all'](_zypper('-x', 'mr', *cmd_opt),
|
|
||||||
- output_loglevel='trace')
|
|
||||||
- _zypper_check_result(ret, xml=True)
|
|
||||||
+ __zypper__.refreshable.xml.call('mr', *cmd_opt)
|
|
||||||
|
|
||||||
# If repo nor added neither modified, error should be thrown
|
|
||||||
if not added and not cmd_opt:
|
|
||||||
@@ -637,9 +798,8 @@ def refresh_db():
|
|
||||||
|
|
||||||
salt '*' pkg.refresh_db
|
|
||||||
'''
|
|
||||||
- cmd = _zypper('refresh', '--force')
|
|
||||||
ret = {}
|
|
||||||
- out = _zypper_check_result(__salt__['cmd.run_all'](cmd, output_loglevel='trace'))
|
|
||||||
+ out = __zypper__.refreshable.call('refresh', '--force')
|
|
||||||
|
|
||||||
for line in out.splitlines():
|
|
||||||
if not line:
|
|
||||||
@@ -779,8 +939,7 @@ def install(name=None,
|
|
||||||
log.info('Targeting repo {0!r}'.format(fromrepo))
|
|
||||||
else:
|
|
||||||
fromrepoopt = ''
|
|
||||||
- cmd_install = _zypper()
|
|
||||||
- cmd_install += ['install', '--name', '--auto-agree-with-licenses']
|
|
||||||
+ cmd_install = ['install', '--name', '--auto-agree-with-licenses']
|
|
||||||
if downloadonly:
|
|
||||||
cmd_install.append('--download-only')
|
|
||||||
if fromrepo:
|
|
||||||
@@ -790,9 +949,7 @@ def install(name=None,
|
|
||||||
while targets:
|
|
||||||
cmd = cmd_install + targets[:500]
|
|
||||||
targets = targets[500:]
|
|
||||||
- call = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False)
|
|
||||||
- out = _zypper_check_result(call)
|
|
||||||
- for line in out.splitlines():
|
|
||||||
+ for line in __zypper__.call(*cmd).splitlines():
|
|
||||||
match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
|
|
||||||
if match:
|
|
||||||
downgrades.append(match.group(1))
|
|
||||||
@@ -800,8 +957,7 @@ def install(name=None,
|
|
||||||
while downgrades:
|
|
||||||
cmd = cmd_install + ['--force'] + downgrades[:500]
|
|
||||||
downgrades = downgrades[500:]
|
|
||||||
-
|
|
||||||
- _zypper_check_result(__salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False))
|
|
||||||
+ __zypper__.call(*cmd)
|
|
||||||
|
|
||||||
__context__.pop('pkg.list_pkgs', None)
|
|
||||||
new = list_pkgs()
|
|
||||||
@@ -837,18 +993,15 @@ def upgrade(refresh=True):
|
|
||||||
if refresh:
|
|
||||||
refresh_db()
|
|
||||||
old = list_pkgs()
|
|
||||||
- cmd = _zypper('update', '--auto-agree-with-licenses')
|
|
||||||
- call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
|
||||||
- if _is_zypper_error(call['retcode']):
|
|
||||||
+ __zypper__.noraise.call('update', '--auto-agree-with-licenses')
|
|
||||||
+ if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES:
|
|
||||||
ret['result'] = False
|
|
||||||
- if 'stderr' in call:
|
|
||||||
- ret['comment'] += call['stderr']
|
|
||||||
- if 'stdout' in call:
|
|
||||||
- ret['comment'] += call['stdout']
|
|
||||||
+ ret['comment'] = (__zypper__.stdout() + os.linesep + __zypper__.stderr()).strip()
|
|
||||||
else:
|
|
||||||
__context__.pop('pkg.list_pkgs', None)
|
|
||||||
new = list_pkgs()
|
|
||||||
ret['changes'] = salt.utils.compare_dicts(old, new)
|
|
||||||
+
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
@@ -868,8 +1021,7 @@ def _uninstall(name=None, pkgs=None):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
while targets:
|
|
||||||
- _zypper_check_result(__salt__['cmd.run_all'](_zypper('remove', *targets[:500]),
|
|
||||||
- output_loglevel='trace'))
|
|
||||||
+ __zypper__.call('remove', *targets[:500])
|
|
||||||
targets = targets[500:]
|
|
||||||
__context__.pop('pkg.list_pkgs', None)
|
|
||||||
|
|
||||||
@@ -982,9 +1134,7 @@ def clean_locks():
|
|
||||||
if not os.path.exists("/etc/zypp/locks"):
|
|
||||||
return out
|
|
||||||
|
|
||||||
- ret = __salt__['cmd.run_all'](_zypper('-x', 'cl'), output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(ret, xml=True))
|
|
||||||
- for node in doc.getElementsByTagName("message"):
|
|
||||||
+ for node in __zypper__.xml.call('cl').getElementsByTagName("message"):
|
|
||||||
text = node.childNodes[0].nodeValue.lower()
|
|
||||||
if text.startswith(LCK):
|
|
||||||
out[LCK] = text.split(" ")[1]
|
|
||||||
@@ -1021,8 +1171,7 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument
|
|
||||||
missing.append(pkg)
|
|
||||||
|
|
||||||
if removed:
|
|
||||||
- _zypper_check_result(__salt__['cmd.run_all'](_zypper('rl', *removed),
|
|
||||||
- output_loglevel='trace'))
|
|
||||||
+ __zypper__.call('rl', *removed)
|
|
||||||
|
|
||||||
return {'removed': len(removed), 'not_found': missing}
|
|
||||||
|
|
||||||
@@ -1051,8 +1200,7 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument
|
|
||||||
added.append(pkg)
|
|
||||||
|
|
||||||
if added:
|
|
||||||
- _zypper_check_result(__salt__['cmd.run_all'](_zypper('al', *added),
|
|
||||||
- output_loglevel='trace'))
|
|
||||||
+ __zypper__.call('al', *added)
|
|
||||||
|
|
||||||
return {'added': len(added), 'packages': added}
|
|
||||||
|
|
||||||
@@ -1185,10 +1333,7 @@ def _get_patterns(installed_only=None):
|
|
||||||
'''
|
|
||||||
patterns = {}
|
|
||||||
|
|
||||||
- ret = __salt__['cmd.run_all'](_zypper('--xmlout', 'se', '-t', 'pattern'),
|
|
||||||
- output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(ret, xml=True))
|
|
||||||
- for element in doc.getElementsByTagName('solvable'):
|
|
||||||
+ for element in __zypper__.nolock.xml.call('se', '-t', 'pattern').getElementsByTagName('solvable'):
|
|
||||||
installed = element.getAttribute('status') == 'installed'
|
|
||||||
if (installed_only and installed) or not installed_only:
|
|
||||||
patterns[element.getAttribute('name')] = {
|
|
||||||
@@ -1251,20 +1396,16 @@ def search(criteria, refresh=False):
|
|
||||||
if refresh:
|
|
||||||
refresh_db()
|
|
||||||
|
|
||||||
- ret = __salt__['cmd.run_all'](_zypper('--xmlout', 'se', criteria),
|
|
||||||
- output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(ret, xml=True))
|
|
||||||
- solvables = doc.getElementsByTagName('solvable')
|
|
||||||
+ solvables = __zypper__.nolock.xml.call('se', criteria).getElementsByTagName('solvable')
|
|
||||||
if not solvables:
|
|
||||||
raise CommandExecutionError('No packages found by criteria "{0}".'.format(criteria))
|
|
||||||
|
|
||||||
out = {}
|
|
||||||
- for solvable in [s for s in solvables
|
|
||||||
- if s.getAttribute('status') == 'not-installed' and
|
|
||||||
- s.getAttribute('kind') == 'package']:
|
|
||||||
- out[solvable.getAttribute('name')] = {
|
|
||||||
- 'summary': solvable.getAttribute('summary')
|
|
||||||
- }
|
|
||||||
+ for solvable in [slv for slv in solvables
|
|
||||||
+ if slv.getAttribute('status') == 'not-installed'
|
|
||||||
+ and slv.getAttribute('kind') == 'package']:
|
|
||||||
+ out[solvable.getAttribute('name')] = {'summary': solvable.getAttribute('summary')}
|
|
||||||
+
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1309,16 +1450,14 @@ def list_products(all=False, refresh=False):
|
|
||||||
|
|
||||||
ret = list()
|
|
||||||
OEM_PATH = "/var/lib/suseRegister/OEM"
|
|
||||||
- cmd = _zypper()
|
|
||||||
+ cmd = list()
|
|
||||||
if not all:
|
|
||||||
cmd.append('--disable-repos')
|
|
||||||
- cmd.extend(['-x', 'products'])
|
|
||||||
+ cmd.append('products')
|
|
||||||
if not all:
|
|
||||||
cmd.append('-i')
|
|
||||||
|
|
||||||
- call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(call, xml=True))
|
|
||||||
- product_list = doc.getElementsByTagName('product-list')
|
|
||||||
+ product_list = __zypper__.nolock.xml.call(*cmd).getElementsByTagName('product-list')
|
|
||||||
if not product_list:
|
|
||||||
return ret # No products found
|
|
||||||
|
|
||||||
@@ -1371,10 +1510,8 @@ def download(*packages, **kwargs):
|
|
||||||
if refresh:
|
|
||||||
refresh_db()
|
|
||||||
|
|
||||||
- ret = __salt__['cmd.run_all'](_zypper('-x', 'download', *packages), output_loglevel='trace')
|
|
||||||
- doc = dom.parseString(_zypper_check_result(ret, xml=True))
|
|
||||||
pkg_ret = {}
|
|
||||||
- for dld_result in doc.getElementsByTagName("download-result"):
|
|
||||||
+ for dld_result in __zypper__.xml.call('download', *packages).getElementsByTagName("download-result"):
|
|
||||||
repo = dld_result.getElementsByTagName("repository")[0]
|
|
||||||
pkg_info = {
|
|
||||||
'repository-name': repo.getAttribute("name"),
|
|
||||||
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
|
||||||
index 97e42ef..16e8542 100644
|
|
||||||
--- a/tests/unit/modules/zypper_test.py
|
|
||||||
+++ b/tests/unit/modules/zypper_test.py
|
|
||||||
@@ -23,6 +23,17 @@ from salttesting.helpers import ensure_in_syspath
|
|
||||||
ensure_in_syspath('../../')
|
|
||||||
|
|
||||||
|
|
||||||
+class ZyppCallMock(object):
|
|
||||||
+ def __init__(self, return_value=None):
|
|
||||||
+ self.__return_value = return_value
|
|
||||||
+
|
|
||||||
+ def __getattr__(self, item):
|
|
||||||
+ return self
|
|
||||||
+
|
|
||||||
+ def __call__(self, *args, **kwargs):
|
|
||||||
+ return MagicMock(return_value=self.__return_value)()
|
|
||||||
+
|
|
||||||
+
|
|
||||||
def get_test_data(filename):
|
|
||||||
'''
|
|
||||||
Return static test data
|
|
||||||
@@ -64,56 +75,63 @@ class ZypperTestCase(TestCase):
|
|
||||||
self.assertIn(pkg, upgrades)
|
|
||||||
self.assertEqual(upgrades[pkg], version)
|
|
||||||
|
|
||||||
- def test_zypper_check_result(self):
|
|
||||||
+ def test_zypper_caller(self):
|
|
||||||
'''
|
|
||||||
- Test zypper check result function
|
|
||||||
+ Test Zypper caller.
|
|
||||||
+ :return:
|
|
||||||
'''
|
|
||||||
- cmd_out = {
|
|
||||||
- 'retcode': 1,
|
|
||||||
- 'stdout': '',
|
|
||||||
- 'stderr': 'This is an error'
|
|
||||||
- }
|
|
||||||
- with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: This is an error$"):
|
|
||||||
- zypper._zypper_check_result(cmd_out)
|
|
||||||
-
|
|
||||||
- cmd_out = {
|
|
||||||
- 'retcode': 0,
|
|
||||||
- 'stdout': 'result',
|
|
||||||
- 'stderr': ''
|
|
||||||
- }
|
|
||||||
- out = zypper._zypper_check_result(cmd_out)
|
|
||||||
- self.assertEqual(out, "result")
|
|
||||||
-
|
|
||||||
- cmd_out = {
|
|
||||||
- 'retcode': 1,
|
|
||||||
- 'stdout': '',
|
|
||||||
- 'stderr': 'This is an error'
|
|
||||||
- }
|
|
||||||
- with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: This is an error$"):
|
|
||||||
- zypper._zypper_check_result(cmd_out, xml=True)
|
|
||||||
-
|
|
||||||
- cmd_out = {
|
|
||||||
- 'retcode': 1,
|
|
||||||
- 'stdout': '',
|
|
||||||
- 'stderr': ''
|
|
||||||
- }
|
|
||||||
- with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: Check zypper logs$"):
|
|
||||||
- zypper._zypper_check_result(cmd_out, xml=True)
|
|
||||||
-
|
|
||||||
- cmd_out = {
|
|
||||||
- 'stdout': '''<?xml version='1.0'?>
|
|
||||||
-<stream>
|
|
||||||
- <message type="info">Refreshing service 'container-suseconnect'.</message>
|
|
||||||
- <message type="error">Some handled zypper internal error</message>
|
|
||||||
- <message type="error">Another zypper internal error</message>
|
|
||||||
-</stream>
|
|
||||||
- ''',
|
|
||||||
- 'stderr': '',
|
|
||||||
- 'retcode': 1
|
|
||||||
- }
|
|
||||||
- with self.assertRaisesRegexp(CommandExecutionError,
|
|
||||||
- "^zypper command failed: Some handled zypper internal error\nAnother zypper internal error$"):
|
|
||||||
- zypper._zypper_check_result(cmd_out, xml=True)
|
|
||||||
+ class RunSniffer(object):
|
|
||||||
+ def __init__(self, stdout=None, stderr=None, retcode=None):
|
|
||||||
+ self.calls = list()
|
|
||||||
+ self._stdout = stdout or ''
|
|
||||||
+ self._stderr = stderr or ''
|
|
||||||
+ self._retcode = retcode or 0
|
|
||||||
+
|
|
||||||
+ def __call__(self, *args, **kwargs):
|
|
||||||
+ self.calls.append({'args': args, 'kwargs': kwargs})
|
|
||||||
+ return {'stdout': self._stdout,
|
|
||||||
+ 'stderr': self._stderr,
|
|
||||||
+ 'retcode': self._retcode}
|
|
||||||
+
|
|
||||||
+ stdout_xml_snippet = '<?xml version="1.0"?><test foo="bar"/>'
|
|
||||||
+ sniffer = RunSniffer(stdout=stdout_xml_snippet)
|
|
||||||
+ with patch.dict('salt.modules.zypper.__salt__', {'cmd.run_all': sniffer}):
|
|
||||||
+ self.assertEqual(zypper.__zypper__.call('foo'), stdout_xml_snippet)
|
|
||||||
+ self.assertEqual(len(sniffer.calls), 1)
|
|
||||||
+
|
|
||||||
+ zypper.__zypper__.call('bar')
|
|
||||||
+ self.assertEqual(len(sniffer.calls), 2)
|
|
||||||
+ self.assertEqual(sniffer.calls[0]['args'][0], ['zypper', '--non-interactive', '--no-refresh', 'foo'])
|
|
||||||
+ self.assertEqual(sniffer.calls[1]['args'][0], ['zypper', '--non-interactive', '--no-refresh', 'bar'])
|
|
||||||
+
|
|
||||||
+ dom = zypper.__zypper__.xml.call('xml-test')
|
|
||||||
+ self.assertEqual(sniffer.calls[2]['args'][0], ['zypper', '--non-interactive', '--xmlout',
|
|
||||||
+ '--no-refresh', 'xml-test'])
|
|
||||||
+ self.assertEqual(dom.getElementsByTagName('test')[0].getAttribute('foo'), 'bar')
|
|
||||||
+
|
|
||||||
+ zypper.__zypper__.refreshable.call('refresh-test')
|
|
||||||
+ self.assertEqual(sniffer.calls[3]['args'][0], ['zypper', '--non-interactive', 'refresh-test'])
|
|
||||||
+
|
|
||||||
+ zypper.__zypper__.nolock.call('no-locking-test')
|
|
||||||
+ self.assertEqual(sniffer.calls[4].get('kwargs', {}).get('env', {}).get('ZYPP_READONLY_HACK'), "1")
|
|
||||||
+ self.assertEqual(sniffer.calls[4].get('kwargs', {}).get('env', {}).get('SALT_RUNNING'), "1")
|
|
||||||
+
|
|
||||||
+ zypper.__zypper__.call('locking-test')
|
|
||||||
+ self.assertEqual(sniffer.calls[5].get('kwargs', {}).get('env', {}).get('ZYPP_READONLY_HACK'), None)
|
|
||||||
+ self.assertEqual(sniffer.calls[5].get('kwargs', {}).get('env', {}).get('SALT_RUNNING'), "1")
|
|
||||||
+
|
|
||||||
+ # Test exceptions
|
|
||||||
+ stdout_xml_snippet = '<?xml version="1.0"?><stream><message type="error">Booya!</message></stream>'
|
|
||||||
+ sniffer = RunSniffer(stdout=stdout_xml_snippet, retcode=1)
|
|
||||||
+ with patch.dict('salt.modules.zypper.__salt__', {'cmd.run_all': sniffer}):
|
|
||||||
+ with self.assertRaisesRegexp(CommandExecutionError, '^Zypper command failure: Booya!$'):
|
|
||||||
+ zypper.__zypper__.xml.call('crashme')
|
|
||||||
+
|
|
||||||
+ with self.assertRaisesRegexp(CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$"):
|
|
||||||
+ zypper.__zypper__.call('crashme again')
|
|
||||||
+
|
|
||||||
+ zypper.__zypper__.noraise.call('stay quiet')
|
|
||||||
+ self.assertEqual(zypper.__zypper__.error_msg, "Check Zypper's logs.")
|
|
||||||
|
|
||||||
def test_list_upgrades_error_handling(self):
|
|
||||||
'''
|
|
||||||
@@ -129,11 +147,12 @@ class ZypperTestCase(TestCase):
|
|
||||||
<message type="error">Another zypper internal error</message>
|
|
||||||
</stream>
|
|
||||||
''',
|
|
||||||
- 'retcode': 1
|
|
||||||
+ 'stderr': '',
|
|
||||||
+ 'retcode': 1,
|
|
||||||
}
|
|
||||||
- with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
|
||||||
+ with patch.dict('salt.modules.zypper.__salt__', {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
|
||||||
with self.assertRaisesRegexp(CommandExecutionError,
|
|
||||||
- "^zypper command failed: Some handled zypper internal error\nAnother zypper internal error$"):
|
|
||||||
+ "^Zypper command failure: Some handled zypper internal error\nAnother zypper internal error$"):
|
|
||||||
zypper.list_upgrades(refresh=False)
|
|
||||||
|
|
||||||
# Test unhandled error
|
|
||||||
@@ -142,8 +161,8 @@ class ZypperTestCase(TestCase):
|
|
||||||
'stdout': '',
|
|
||||||
'stderr': ''
|
|
||||||
}
|
|
||||||
- with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
|
||||||
- with self.assertRaisesRegexp(CommandExecutionError, '^zypper command failed: Check zypper logs$'):
|
|
||||||
+ with patch.dict('salt.modules.zypper.__salt__', {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
|
||||||
+ with self.assertRaisesRegexp(CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$"):
|
|
||||||
zypper.list_upgrades(refresh=False)
|
|
||||||
|
|
||||||
def test_list_products(self):
|
|
||||||
@@ -260,8 +279,7 @@ class ZypperTestCase(TestCase):
|
|
||||||
:return:
|
|
||||||
'''
|
|
||||||
test_pkgs = ['vim', 'emacs', 'python']
|
|
||||||
- ref_out = get_test_data('zypper-available.txt')
|
|
||||||
- with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
|
|
||||||
+ with patch('salt.modules.zypper.__zypper__', ZyppCallMock(return_value=get_test_data('zypper-available.txt'))):
|
|
||||||
available = zypper.info_available(*test_pkgs, refresh=False)
|
|
||||||
self.assertEqual(len(available), 3)
|
|
||||||
for pkg_name, pkg_info in available.items():
|
|
||||||
@@ -286,8 +304,7 @@ class ZypperTestCase(TestCase):
|
|
||||||
|
|
||||||
:return:
|
|
||||||
'''
|
|
||||||
- ref_out = get_test_data('zypper-available.txt')
|
|
||||||
- with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
|
|
||||||
+ with patch('salt.modules.zypper.__zypper__', ZyppCallMock(return_value=get_test_data('zypper-available.txt'))):
|
|
||||||
self.assertEqual(zypper.latest_version('vim'), '7.4.326-2.62')
|
|
||||||
|
|
||||||
@patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
|
|
||||||
@@ -298,7 +315,7 @@ class ZypperTestCase(TestCase):
|
|
||||||
:return:
|
|
||||||
'''
|
|
||||||
ref_out = get_test_data('zypper-available.txt')
|
|
||||||
- with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
|
|
||||||
+ with patch('salt.modules.zypper.__zypper__', ZyppCallMock(return_value=get_test_data('zypper-available.txt'))):
|
|
||||||
for pkg_name in ['emacs', 'python']:
|
|
||||||
self.assertFalse(zypper.upgrade_available(pkg_name))
|
|
||||||
self.assertTrue(zypper.upgrade_available('vim'))
|
|
||||||
--
|
|
||||||
2.8.1
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
|||||||
From e52b55979bdc0734c2e452dd2fd67fb56a3fb37b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
|
||||||
Date: Fri, 6 May 2016 12:29:48 +0200
|
|
||||||
Subject: [PATCH 16/16] Bugfix: Restore boolean values from the repo
|
|
||||||
configuration
|
|
||||||
|
|
||||||
* Add test data for repos
|
|
||||||
|
|
||||||
* Add repo config test
|
|
||||||
|
|
||||||
* Bugfix (follow-up): setting priority requires non-positive integer
|
|
||||||
---
|
|
||||||
salt/modules/zypper.py | 16 +++++++++-------
|
|
||||||
tests/unit/modules/zypp/zypper-repo-1.cfg | 5 +++++
|
|
||||||
tests/unit/modules/zypp/zypper-repo-2.cfg | 5 +++++
|
|
||||||
tests/unit/modules/zypper_test.py | 21 +++++++++++++++++++++
|
|
||||||
4 files changed, 40 insertions(+), 7 deletions(-)
|
|
||||||
create mode 100644 tests/unit/modules/zypp/zypper-repo-1.cfg
|
|
||||||
create mode 100644 tests/unit/modules/zypp/zypper-repo-2.cfg
|
|
||||||
|
|
||||||
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
|
||||||
index 53b5d9f..c37b382 100644
|
|
||||||
--- a/salt/modules/zypper.py
|
|
||||||
+++ b/salt/modules/zypper.py
|
|
||||||
@@ -602,12 +602,14 @@ def _get_repo_info(alias, repos_cfg=None):
|
|
||||||
Get one repo meta-data.
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
- ret = dict((repos_cfg or _get_configured_repos()).items(alias))
|
|
||||||
- ret['alias'] = alias
|
|
||||||
- for key, val in six.iteritems(ret):
|
|
||||||
- if val == 'NONE':
|
|
||||||
- ret[key] = None
|
|
||||||
- return ret
|
|
||||||
+ meta = dict((repos_cfg or _get_configured_repos()).items(alias))
|
|
||||||
+ meta['alias'] = alias
|
|
||||||
+ for key, val in six.iteritems(meta):
|
|
||||||
+ if val in ['0', '1']:
|
|
||||||
+ meta[key] = int(meta[key]) == 1
|
|
||||||
+ elif val == 'NONE':
|
|
||||||
+ meta[key] = None
|
|
||||||
+ return meta
|
|
||||||
except (ValueError, configparser.NoSectionError):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
@@ -769,7 +771,7 @@ def mod_repo(repo, **kwargs):
|
|
||||||
cmd_opt.append('--gpg-auto-import-keys')
|
|
||||||
|
|
||||||
if 'priority' in kwargs:
|
|
||||||
- cmd_opt.append("--priority='{0}'".format(kwargs.get('priority', DEFAULT_PRIORITY)))
|
|
||||||
+ cmd_opt.append("--priority={0}".format(kwargs.get('priority', DEFAULT_PRIORITY)))
|
|
||||||
|
|
||||||
if 'humanname' in kwargs:
|
|
||||||
cmd_opt.append("--name='{0}'".format(kwargs.get('humanname')))
|
|
||||||
diff --git a/tests/unit/modules/zypp/zypper-repo-1.cfg b/tests/unit/modules/zypp/zypper-repo-1.cfg
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..958718c
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/unit/modules/zypp/zypper-repo-1.cfg
|
|
||||||
@@ -0,0 +1,5 @@
|
|
||||||
+[SLE12-SP1-x86_64-Update]
|
|
||||||
+enabled=1
|
|
||||||
+autorefresh=1
|
|
||||||
+baseurl=http://somehost.com/SUSE/Updates/SLE-SERVER/12-SP1/x86_64/update/
|
|
||||||
+type=NONE
|
|
||||||
diff --git a/tests/unit/modules/zypp/zypper-repo-2.cfg b/tests/unit/modules/zypp/zypper-repo-2.cfg
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..f55cf18
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/unit/modules/zypp/zypper-repo-2.cfg
|
|
||||||
@@ -0,0 +1,5 @@
|
|
||||||
+[SLE12-SP1-x86_64-Update-disabled]
|
|
||||||
+enabled=0
|
|
||||||
+autorefresh=0
|
|
||||||
+baseurl=http://somehost.com/SUSE/Updates/SLE-SERVER/12-SP1/x86_64/update/
|
|
||||||
+type=NONE
|
|
||||||
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
|
|
||||||
index 16e8542..4e735cd 100644
|
|
||||||
--- a/tests/unit/modules/zypper_test.py
|
|
||||||
+++ b/tests/unit/modules/zypper_test.py
|
|
||||||
@@ -17,6 +17,8 @@ from salttesting.mock import (
|
|
||||||
from salt.exceptions import CommandExecutionError
|
|
||||||
|
|
||||||
import os
|
|
||||||
+from salt.ext.six.moves import configparser
|
|
||||||
+import StringIO
|
|
||||||
|
|
||||||
from salttesting.helpers import ensure_in_syspath
|
|
||||||
|
|
||||||
@@ -391,6 +393,25 @@ class ZypperTestCase(TestCase):
|
|
||||||
self.assertTrue(diff[pkg_name]['old'])
|
|
||||||
self.assertFalse(diff[pkg_name]['new'])
|
|
||||||
|
|
||||||
+ def test_repo_value_info(self):
|
|
||||||
+ '''
|
|
||||||
+ Tests if repo info is properly parsed.
|
|
||||||
+
|
|
||||||
+ :return:
|
|
||||||
+ '''
|
|
||||||
+ repos_cfg = configparser.ConfigParser()
|
|
||||||
+ for cfg in ['zypper-repo-1.cfg', 'zypper-repo-2.cfg']:
|
|
||||||
+ repos_cfg.readfp(StringIO.StringIO(get_test_data(cfg)))
|
|
||||||
+
|
|
||||||
+ for alias in repos_cfg.sections():
|
|
||||||
+ r_info = zypper._get_repo_info(alias, repos_cfg=repos_cfg)
|
|
||||||
+ self.assertEqual(type(r_info['type']), type(None))
|
|
||||||
+ self.assertEqual(type(r_info['enabled']), bool)
|
|
||||||
+ self.assertEqual(type(r_info['autorefresh']), bool)
|
|
||||||
+ self.assertEqual(type(r_info['baseurl']), str)
|
|
||||||
+ self.assertEqual(r_info['type'], None)
|
|
||||||
+ self.assertEqual(r_info['enabled'], alias == 'SLE12-SP1-x86_64-Update')
|
|
||||||
+ self.assertEqual(r_info['autorefresh'], alias == 'SLE12-SP1-x86_64-Update')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from integration import run_tests
|
|
||||||
--
|
|
||||||
2.8.1
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
|||||||
|
From 50865e300e6e90c5cc80c8878949a2f3bcaaeeec Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bo Maryniuk <bo@suse.de>
|
||||||
|
Date: Thu, 25 Aug 2016 16:47:08 +0200
|
||||||
|
Subject: [PATCH 16/16] Improve Mock to be flexible and able to mock methods
|
||||||
|
from the mocked modules
|
||||||
|
|
||||||
|
* Configure importing Mock to handle 'total' method from psutils properly
|
||||||
|
---
|
||||||
|
doc/conf.py | 37 +++++++++++++++++++++++++------------
|
||||||
|
1 file changed, 25 insertions(+), 12 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/doc/conf.py b/doc/conf.py
|
||||||
|
index 9cefed8..b73ca2a 100644
|
||||||
|
--- a/doc/conf.py
|
||||||
|
+++ b/doc/conf.py
|
||||||
|
@@ -15,31 +15,40 @@ from sphinx.directives import TocTree
|
||||||
|
# pylint: disable=R0903
|
||||||
|
class Mock(object):
|
||||||
|
'''
|
||||||
|
- Mock out specified imports
|
||||||
|
+ Mock out specified imports.
|
||||||
|
|
||||||
|
This allows autodoc to do its thing without having oodles of req'd
|
||||||
|
installed libs. This doesn't work with ``import *`` imports.
|
||||||
|
|
||||||
|
+ This Mock class can be configured to return a specific values at specific names, if required.
|
||||||
|
+
|
||||||
|
http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules
|
||||||
|
'''
|
||||||
|
- def __init__(self, *args, **kwargs):
|
||||||
|
- pass
|
||||||
|
+ def __init__(self, mapping=None, *args, **kwargs):
|
||||||
|
+ """
|
||||||
|
+ Mapping allows to bypass the Mock object, but actually assign
|
||||||
|
+ a specific value, expected by a specific attribute returned.
|
||||||
|
+ """
|
||||||
|
+ self.__mapping = mapping or {}
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
- ret = Mock()
|
||||||
|
# If mocked function is used as a decorator, expose decorated function.
|
||||||
|
# if args and callable(args[-1]):
|
||||||
|
# functools.update_wrapper(ret, args[0])
|
||||||
|
- return ret
|
||||||
|
-
|
||||||
|
- @classmethod
|
||||||
|
- def __getattr__(cls, name):
|
||||||
|
- if name in ('__file__', '__path__'):
|
||||||
|
- return '/dev/null'
|
||||||
|
+ return Mock(mapping=self.__mapping)
|
||||||
|
+
|
||||||
|
+ def __getattr__(self, name):
|
||||||
|
+ #__mapping = {'total': 0}
|
||||||
|
+ data = None
|
||||||
|
+ if name in self.__mapping:
|
||||||
|
+ data = self.__mapping.get(name)
|
||||||
|
+ elif name in ('__file__', '__path__'):
|
||||||
|
+ data = '/dev/null'
|
||||||
|
else:
|
||||||
|
- return Mock()
|
||||||
|
+ data = Mock(mapping=self.__mapping)
|
||||||
|
+ return data
|
||||||
|
# pylint: enable=R0903
|
||||||
|
|
||||||
|
MOCK_MODULES = [
|
||||||
|
@@ -133,7 +142,11 @@ MOCK_MODULES = [
|
||||||
|
]
|
||||||
|
|
||||||
|
for mod_name in MOCK_MODULES:
|
||||||
|
- sys.modules[mod_name] = Mock()
|
||||||
|
+ if mod_name == 'psutil':
|
||||||
|
+ mock = Mock(mapping={'total': 0}) # Otherwise it will crash Sphinx
|
||||||
|
+ else:
|
||||||
|
+ mock = Mock()
|
||||||
|
+ sys.modules[mod_name] = mock
|
||||||
|
|
||||||
|
def mock_decorator_with_params(*oargs, **okwargs):
|
||||||
|
'''
|
||||||
|
--
|
||||||
|
2.9.3
|
||||||
|
|
@ -1,103 +0,0 @@
|
|||||||
From 92f17a79c53bb5b75b9dac4aa0add94dfe2f447f Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bo Maryniuk <bo@suse.de>
|
|
||||||
Date: Mon, 9 May 2016 10:33:44 +0200
|
|
||||||
Subject: [PATCH 17/17] Add SUSE Manager plugin
|
|
||||||
|
|
||||||
---
|
|
||||||
scripts/zypper/plugins/commit/README.md | 3 ++
|
|
||||||
scripts/zypper/plugins/commit/susemanager | 73 +++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 76 insertions(+)
|
|
||||||
create mode 100644 scripts/zypper/plugins/commit/README.md
|
|
||||||
create mode 100755 scripts/zypper/plugins/commit/susemanager
|
|
||||||
|
|
||||||
diff --git a/scripts/zypper/plugins/commit/README.md b/scripts/zypper/plugins/commit/README.md
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..01c8917
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/scripts/zypper/plugins/commit/README.md
|
|
||||||
@@ -0,0 +1,3 @@
|
|
||||||
+# Zypper plugins
|
|
||||||
+
|
|
||||||
+Plugins here are required to interact with SUSE Manager in conjunction of SaltStack and Zypper.
|
|
||||||
diff --git a/scripts/zypper/plugins/commit/susemanager b/scripts/zypper/plugins/commit/susemanager
|
|
||||||
new file mode 100755
|
|
||||||
index 0000000..e64d683
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/scripts/zypper/plugins/commit/susemanager
|
|
||||||
@@ -0,0 +1,73 @@
|
|
||||||
+#!/usr/bin/python
|
|
||||||
+#
|
|
||||||
+# Copyright (c) 2016 SUSE Linux LLC
|
|
||||||
+# All Rights Reserved.
|
|
||||||
+#
|
|
||||||
+# This software is licensed to you under the GNU General Public License,
|
|
||||||
+# version 2 (GPLv2). There is NO WARRANTY for this software, express or
|
|
||||||
+# implied, including the implied warranties of MERCHANTABILITY or FITNESS
|
|
||||||
+# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
|
|
||||||
+# along with this software; if not, see
|
|
||||||
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
|
||||||
+#
|
|
||||||
+# Author: Bo Maryniuk <bo@suse.de>
|
|
||||||
+
|
|
||||||
+import sys
|
|
||||||
+import os
|
|
||||||
+
|
|
||||||
+import salt.client
|
|
||||||
+import salt.utils
|
|
||||||
+
|
|
||||||
+from zypp_plugin import Plugin
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class SpacewalkDriftDetector(Plugin):
|
|
||||||
+ """
|
|
||||||
+ Return diff of the installed packages outside the Salt.
|
|
||||||
+ """
|
|
||||||
+ def __init__(self):
|
|
||||||
+ Plugin.__init__(self)
|
|
||||||
+ self.salt = salt.client.Caller().sminion.functions
|
|
||||||
+
|
|
||||||
+ def _within_salt(self):
|
|
||||||
+ """
|
|
||||||
+ Return true, if Zypper is running from within the SaltStack.
|
|
||||||
+ """
|
|
||||||
+ return 'SALT_RUNNING' in os.environ
|
|
||||||
+
|
|
||||||
+ def _get_packages(self):
|
|
||||||
+ """
|
|
||||||
+ Get the list of the packages at the current time.
|
|
||||||
+ """
|
|
||||||
+ ret = dict()
|
|
||||||
+ cmd = "rpm -qa --queryformat '%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%|EPOCH?{%{EPOCH}}:{}|\\n'"
|
|
||||||
+ for line in os.popen(cmd).read().split("\n"):
|
|
||||||
+ if not line:
|
|
||||||
+ continue
|
|
||||||
+ name, pkgver, rel, epoch = line.split('_|-')
|
|
||||||
+ if epoch:
|
|
||||||
+ pkgver = '{0}:{1}'.format(epoch, pkgver)
|
|
||||||
+ if rel:
|
|
||||||
+ pkgver += '-{0}'.format(rel)
|
|
||||||
+ ret[name] = pkgver
|
|
||||||
+
|
|
||||||
+ return ret
|
|
||||||
+
|
|
||||||
+ def PLUGINBEGIN(self, headers, body):
|
|
||||||
+ """
|
|
||||||
+ Hook when plugin begins Zypper's transaction.
|
|
||||||
+ """
|
|
||||||
+ if not self._within_salt():
|
|
||||||
+ self._pkg_before = self._get_packages()
|
|
||||||
+ self.ack()
|
|
||||||
+
|
|
||||||
+ def PLUGINEND(self, headers, body):
|
|
||||||
+ """
|
|
||||||
+ Hook when plugin closes Zypper's transaction.
|
|
||||||
+ """
|
|
||||||
+ if not self._within_salt():
|
|
||||||
+ self.salt['event.send']('zypper/changed', salt.utils.compare_dicts(self._pkg_before, self._get_packages()))
|
|
||||||
+ self.ack()
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+SpacewalkDriftDetector().main()
|
|
||||||
--
|
|
||||||
2.8.2
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
From e9b4a199f48abc94be71082c56b6b059c6694dc0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Eric Jackson <swiftgist@gmail.com>
|
||||||
|
Date: Tue, 30 Aug 2016 15:47:07 -0400
|
||||||
|
Subject: [PATCH 17/17] Check for single quote before splitting on single quote
|
||||||
|
|
||||||
|
Signed-off-by: Eric Jackson <ejackson@suse.com>
|
||||||
|
|
||||||
|
Lint for #35916
|
||||||
|
|
||||||
|
Merges #35916
|
||||||
|
---
|
||||||
|
salt/modules/zypper.py | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
|
index 1ec903e..4bb9a09 100644
|
||||||
|
--- a/salt/modules/zypper.py
|
||||||
|
+++ b/salt/modules/zypper.py
|
||||||
|
@@ -859,11 +859,11 @@ def refresh_db():
|
||||||
|
for line in out.splitlines():
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
- if line.strip().startswith('Repository'):
|
||||||
|
+ if line.strip().startswith('Repository') and '\'' in line:
|
||||||
|
key = line.split('\'')[1].strip()
|
||||||
|
if 'is up to date' in line:
|
||||||
|
ret[key] = False
|
||||||
|
- elif line.strip().startswith('Building'):
|
||||||
|
+ elif line.strip().startswith('Building') and '\'' in line:
|
||||||
|
key = line.split('\'')[1].strip()
|
||||||
|
if 'done' in line:
|
||||||
|
ret[key] = True
|
||||||
|
--
|
||||||
|
2.8.2
|
||||||
|
|
927
0018-Unit-tests-fixes-for-2016.3.2.patch
Normal file
927
0018-Unit-tests-fixes-for-2016.3.2.patch
Normal file
@ -0,0 +1,927 @@
|
|||||||
|
From e5fc36b5fad0683f57022bf2f3c63f453cda5e8d Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Tue, 6 Sep 2016 11:21:05 +0100
|
||||||
|
Subject: [PATCH 18/19] Unit tests fixes for 2016.3.2
|
||||||
|
|
||||||
|
* Fixing skipped boto tests to prevent errors if boto3 does not exists.
|
||||||
|
* Fix tests that assert CommandExecutionError (#32485)
|
||||||
|
Trying to assert that an exception was raised using
|
||||||
|
helper_open.write.assertRaises() is bogus--there is no such method. Use
|
||||||
|
standard unittest.assertRaises() instead.
|
||||||
|
* Skip utils_test if timelib is not installed (#32699)
|
||||||
|
date_cast() throws a RuntimeError, not an ImportError
|
||||||
|
* Fix tests (#35693)
|
||||||
|
Fix tests/unit/modules/useradd_test.py::UserAddTestCase::test_info
|
||||||
|
Fix unit/pyobjects_test.py::MapTests::test_map
|
||||||
|
Fix tests/unit/pyobjects_test.py::RendererTests::test_extend
|
||||||
|
Fix tests/unit/pyobjects_test.py::RendererTests::test_requisite_implicit_list
|
||||||
|
* Fix tests to prevent errors when libcloud is not present
|
||||||
|
* Fixed _interfaces_ifconfig output for SunOS test
|
||||||
|
* Fix PortageConfigTestCase in case of portage is not present
|
||||||
|
* Rename dockerio.py unit tests to dockerio_test.py
|
||||||
|
These tests have never run automatically because of an incorrect file name.
|
||||||
|
Added a skipIf on these tests as they are currently non-functioning and the
|
||||||
|
module they're testing has been deprecated.
|
||||||
|
* Prevent tests failures if boto does not exists
|
||||||
|
---
|
||||||
|
salt/modules/boto_elb.py | 2 +-
|
||||||
|
salt/modules/linux_sysctl.py | 6 +-
|
||||||
|
tests/unit/cloud/clouds/dimensiondata_test.py | 10 ++-
|
||||||
|
tests/unit/cloud/clouds/gce_test.py | 10 ++-
|
||||||
|
tests/unit/modules/boto_cloudtrail_test.py | 10 +--
|
||||||
|
tests/unit/modules/boto_iot_test.py | 10 +--
|
||||||
|
tests/unit/modules/boto_lambda_test.py | 10 +--
|
||||||
|
tests/unit/modules/boto_s3_bucket_test.py | 10 +--
|
||||||
|
tests/unit/modules/boto_secgroup_test.py | 1 +
|
||||||
|
tests/unit/modules/boto_vpc_test.py | 14 ++--
|
||||||
|
tests/unit/modules/linux_sysctl_test.py | 19 +++--
|
||||||
|
tests/unit/modules/mac_sysctl_test.py | 10 +--
|
||||||
|
tests/unit/modules/mount_test.py | 14 ++--
|
||||||
|
tests/unit/modules/portage_config.py | 10 ++-
|
||||||
|
tests/unit/modules/puppet_test.py | 15 ++--
|
||||||
|
tests/unit/modules/useradd_test.py | 6 +-
|
||||||
|
tests/unit/pyobjects_test.py | 11 +++
|
||||||
|
tests/unit/states/boto_cloudtrail_test.py | 10 +--
|
||||||
|
tests/unit/states/boto_iot_test.py | 10 +--
|
||||||
|
tests/unit/states/boto_lambda_test.py | 10 +--
|
||||||
|
tests/unit/states/boto_s3_bucket_test.py | 10 +--
|
||||||
|
tests/unit/states/dockerio.py | 112 -------------------------
|
||||||
|
tests/unit/states/dockerio_test.py | 113 ++++++++++++++++++++++++++
|
||||||
|
tests/unit/utils/network.py | 12 +--
|
||||||
|
tests/unit/utils/utils_test.py | 11 +--
|
||||||
|
25 files changed, 244 insertions(+), 212 deletions(-)
|
||||||
|
delete mode 100644 tests/unit/states/dockerio.py
|
||||||
|
create mode 100644 tests/unit/states/dockerio_test.py
|
||||||
|
|
||||||
|
diff --git a/salt/modules/boto_elb.py b/salt/modules/boto_elb.py
|
||||||
|
index 31df1fc..162abcd 100644
|
||||||
|
--- a/salt/modules/boto_elb.py
|
||||||
|
+++ b/salt/modules/boto_elb.py
|
||||||
|
@@ -57,6 +57,7 @@ log = logging.getLogger(__name__)
|
||||||
|
# Import third party libs
|
||||||
|
try:
|
||||||
|
import boto
|
||||||
|
+ import boto.ec2 # pylint: enable=unused-import
|
||||||
|
# connection settings were added in 2.33.0
|
||||||
|
required_boto_version = '2.33.0'
|
||||||
|
if (_LooseVersion(boto.__version__) <
|
||||||
|
@@ -64,7 +65,6 @@ try:
|
||||||
|
msg = 'boto_elb requires boto {0}.'.format(required_boto_version)
|
||||||
|
logging.debug(msg)
|
||||||
|
raise ImportError()
|
||||||
|
- import boto.ec2
|
||||||
|
from boto.ec2.elb import HealthCheck
|
||||||
|
from boto.ec2.elb.attributes import AccessLogAttribute
|
||||||
|
from boto.ec2.elb.attributes import ConnectionDrainingAttribute
|
||||||
|
diff --git a/salt/modules/linux_sysctl.py b/salt/modules/linux_sysctl.py
|
||||||
|
index b016ca6..7702d52 100644
|
||||||
|
--- a/salt/modules/linux_sysctl.py
|
||||||
|
+++ b/salt/modules/linux_sysctl.py
|
||||||
|
@@ -41,7 +41,11 @@ def _check_systemd_salt_config():
|
||||||
|
sysctl_dir = os.path.split(conf)[0]
|
||||||
|
if not os.path.exists(sysctl_dir):
|
||||||
|
os.makedirs(sysctl_dir)
|
||||||
|
- salt.utils.fopen(conf, 'w').close()
|
||||||
|
+ try:
|
||||||
|
+ salt.utils.fopen(conf, 'w').close()
|
||||||
|
+ except (IOError, OSError):
|
||||||
|
+ msg = 'Could not create file: {0}'
|
||||||
|
+ raise CommandExecutionError(msg.format(conf))
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/tests/unit/cloud/clouds/dimensiondata_test.py b/tests/unit/cloud/clouds/dimensiondata_test.py
|
||||||
|
index aa7f2c0..ee01d65 100644
|
||||||
|
--- a/tests/unit/cloud/clouds/dimensiondata_test.py
|
||||||
|
+++ b/tests/unit/cloud/clouds/dimensiondata_test.py
|
||||||
|
@@ -8,7 +8,13 @@
|
||||||
|
|
||||||
|
# Import Python libs
|
||||||
|
from __future__ import absolute_import
|
||||||
|
-import libcloud.security
|
||||||
|
+
|
||||||
|
+try:
|
||||||
|
+ import libcloud.security
|
||||||
|
+ HAS_LIBCLOUD = True
|
||||||
|
+except ImportError:
|
||||||
|
+ HAS_LIBCLOUD = False
|
||||||
|
+
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
@@ -44,7 +50,7 @@ ON_SUSE = True if 'SuSE' in platform.dist() else False
|
||||||
|
ON_MAC = True if 'Darwin' in platform.system() else False
|
||||||
|
|
||||||
|
if not os.path.exists('/etc/ssl/certs/YaST-CA.pem') and ON_SUSE:
|
||||||
|
- if os.path.isfile('/etc/ssl/ca-bundle.pem'):
|
||||||
|
+ if os.path.isfile('/etc/ssl/ca-bundle.pem') and HAS_LIBCLOUD:
|
||||||
|
libcloud.security.CA_CERTS_PATH.append('/etc/ssl/ca-bundle.pem')
|
||||||
|
else:
|
||||||
|
HAS_CERTS = False
|
||||||
|
diff --git a/tests/unit/cloud/clouds/gce_test.py b/tests/unit/cloud/clouds/gce_test.py
|
||||||
|
index 87824eb..c90f8ab 100644
|
||||||
|
--- a/tests/unit/cloud/clouds/gce_test.py
|
||||||
|
+++ b/tests/unit/cloud/clouds/gce_test.py
|
||||||
|
@@ -8,7 +8,13 @@
|
||||||
|
|
||||||
|
# Import Python libs
|
||||||
|
from __future__ import absolute_import
|
||||||
|
-import libcloud.security
|
||||||
|
+
|
||||||
|
+try:
|
||||||
|
+ import libcloud.security
|
||||||
|
+ HAS_LIBCLOUD = True
|
||||||
|
+except ImportError:
|
||||||
|
+ HAS_LIBCLOUD = False
|
||||||
|
+
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
@@ -51,7 +57,7 @@ ON_SUSE = True if 'SuSE' in platform.dist() else False
|
||||||
|
ON_MAC = True if 'Darwin' in platform.system() else False
|
||||||
|
|
||||||
|
if not os.path.exists('/etc/ssl/certs/YaST-CA.pem') and ON_SUSE:
|
||||||
|
- if os.path.isfile('/etc/ssl/ca-bundle.pem'):
|
||||||
|
+ if os.path.isfile('/etc/ssl/ca-bundle.pem') and HAS_LIBCLOUD:
|
||||||
|
libcloud.security.CA_CERTS_PATH.append('/etc/ssl/ca-bundle.pem')
|
||||||
|
else:
|
||||||
|
HAS_CERTS = False
|
||||||
|
diff --git a/tests/unit/modules/boto_cloudtrail_test.py b/tests/unit/modules/boto_cloudtrail_test.py
|
||||||
|
index 2f86101..264a795 100644
|
||||||
|
--- a/tests/unit/modules/boto_cloudtrail_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_cloudtrail_test.py
|
||||||
|
@@ -103,6 +103,11 @@ if _has_required_boto():
|
||||||
|
StopLoggingTime=None)
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoCloudTrailTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -128,11 +133,6 @@ class BotoCloudTrailTestCaseMixin(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoCloudTrailTestCase(BotoCloudTrailTestCaseBase, BotoCloudTrailTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_cloudtrail module
|
||||||
|
diff --git a/tests/unit/modules/boto_iot_test.py b/tests/unit/modules/boto_iot_test.py
|
||||||
|
index 73c362f..520bfe9 100644
|
||||||
|
--- a/tests/unit/modules/boto_iot_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_iot_test.py
|
||||||
|
@@ -103,6 +103,11 @@ if _has_required_boto():
|
||||||
|
ruleDisabled=True)
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoIoTTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -128,11 +133,6 @@ class BotoIoTTestCaseMixin(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoIoTPolicyTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_iot module
|
||||||
|
diff --git a/tests/unit/modules/boto_lambda_test.py b/tests/unit/modules/boto_lambda_test.py
|
||||||
|
index 01ca245..ad7fb33 100644
|
||||||
|
--- a/tests/unit/modules/boto_lambda_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_lambda_test.py
|
||||||
|
@@ -109,6 +109,11 @@ def _has_required_boto():
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoLambdaTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -145,11 +150,6 @@ class BotoLambdaTestCaseMixin(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoLambdaFunctionTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_lambda module
|
||||||
|
diff --git a/tests/unit/modules/boto_s3_bucket_test.py b/tests/unit/modules/boto_s3_bucket_test.py
|
||||||
|
index f4b1992..5e7d6be 100644
|
||||||
|
--- a/tests/unit/modules/boto_s3_bucket_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_s3_bucket_test.py
|
||||||
|
@@ -205,6 +205,11 @@ if _has_required_boto():
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoS3BucketTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -230,11 +235,6 @@ class BotoS3BucketTestCaseMixin(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoS3BucketTestCase(BotoS3BucketTestCaseBase, BotoS3BucketTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_s3_bucket module
|
||||||
|
diff --git a/tests/unit/modules/boto_secgroup_test.py b/tests/unit/modules/boto_secgroup_test.py
|
||||||
|
index cc88568..7fd51ad 100644
|
||||||
|
--- a/tests/unit/modules/boto_secgroup_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_secgroup_test.py
|
||||||
|
@@ -23,6 +23,7 @@ import salt.loader
|
||||||
|
from salt.ext.six.moves import range # pylint: disable=redefined-builtin
|
||||||
|
try:
|
||||||
|
import boto
|
||||||
|
+ import boto.ec2 # pylint: enable=unused-import
|
||||||
|
HAS_BOTO = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_BOTO = False
|
||||||
|
diff --git a/tests/unit/modules/boto_vpc_test.py b/tests/unit/modules/boto_vpc_test.py
|
||||||
|
index 64c7976..162bcae 100644
|
||||||
|
--- a/tests/unit/modules/boto_vpc_test.py
|
||||||
|
+++ b/tests/unit/modules/boto_vpc_test.py
|
||||||
|
@@ -124,6 +124,13 @@ def _has_required_moto():
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(HAS_MOTO is False, 'The moto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto_version))
|
||||||
|
+@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version))
|
||||||
|
class BotoVpcTestCaseBase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
boto_vpc.__context__ = {}
|
||||||
|
@@ -249,13 +256,6 @@ class BotoVpcTestCaseMixin(object):
|
||||||
|
return rtbl
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(HAS_MOTO is False, 'The moto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto_version))
|
||||||
|
-@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version))
|
||||||
|
class BotoVpcTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_vpc module
|
||||||
|
diff --git a/tests/unit/modules/linux_sysctl_test.py b/tests/unit/modules/linux_sysctl_test.py
|
||||||
|
index 89bea83..1eca7d5 100644
|
||||||
|
--- a/tests/unit/modules/linux_sysctl_test.py
|
||||||
|
+++ b/tests/unit/modules/linux_sysctl_test.py
|
||||||
|
@@ -84,17 +84,22 @@ class LinuxSysctlTestCase(TestCase):
|
||||||
|
self.assertEqual(linux_sysctl.assign(
|
||||||
|
'net.ipv4.ip_forward', 1), ret)
|
||||||
|
|
||||||
|
- @patch('os.path.isfile', MagicMock(return_value=False))
|
||||||
|
def test_persist_no_conf_failure(self):
|
||||||
|
'''
|
||||||
|
Tests adding of config file failure
|
||||||
|
'''
|
||||||
|
- with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- linux_sysctl.persist,
|
||||||
|
- 'net.ipv4.ip_forward',
|
||||||
|
- 1, config=None)
|
||||||
|
+ asn_cmd = {'pid': 1337, 'retcode': 0,
|
||||||
|
+ 'stderr': "sysctl: permission denied", 'stdout': ''}
|
||||||
|
+ mock_asn_cmd = MagicMock(return_value=asn_cmd)
|
||||||
|
+ cmd = "sysctl -w net.ipv4.ip_forward=1"
|
||||||
|
+ mock_cmd = MagicMock(return_value=cmd)
|
||||||
|
+ with patch.dict(linux_sysctl.__salt__, {'cmd.run_stdout': mock_cmd,
|
||||||
|
+ 'cmd.run_all': mock_asn_cmd}):
|
||||||
|
+ with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
+ self.assertRaises(CommandExecutionError,
|
||||||
|
+ linux_sysctl.persist,
|
||||||
|
+ 'net.ipv4.ip_forward',
|
||||||
|
+ 1, config=None)
|
||||||
|
|
||||||
|
@patch('os.path.isfile', MagicMock(return_value=False))
|
||||||
|
@patch('os.path.exists', MagicMock(return_value=True))
|
||||||
|
diff --git a/tests/unit/modules/mac_sysctl_test.py b/tests/unit/modules/mac_sysctl_test.py
|
||||||
|
index e90ec64..533397b 100644
|
||||||
|
--- a/tests/unit/modules/mac_sysctl_test.py
|
||||||
|
+++ b/tests/unit/modules/mac_sysctl_test.py
|
||||||
|
@@ -72,11 +72,11 @@ class DarwinSysctlTestCase(TestCase):
|
||||||
|
Tests adding of config file failure
|
||||||
|
'''
|
||||||
|
with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- mac_sysctl.persist,
|
||||||
|
- 'net.inet.icmp.icmplim',
|
||||||
|
- 50, config=None)
|
||||||
|
+ m_open.side_effect = IOError(13, 'Permission denied', '/file')
|
||||||
|
+ self.assertRaises(CommandExecutionError,
|
||||||
|
+ mac_sysctl.persist,
|
||||||
|
+ 'net.inet.icmp.icmplim',
|
||||||
|
+ 50, config=None)
|
||||||
|
|
||||||
|
@patch('os.path.isfile', MagicMock(return_value=False))
|
||||||
|
def test_persist_no_conf_success(self):
|
||||||
|
diff --git a/tests/unit/modules/mount_test.py b/tests/unit/modules/mount_test.py
|
||||||
|
index 290c368..b2cf904 100644
|
||||||
|
--- a/tests/unit/modules/mount_test.py
|
||||||
|
+++ b/tests/unit/modules/mount_test.py
|
||||||
|
@@ -141,10 +141,10 @@ class MountTestCase(TestCase):
|
||||||
|
with patch.dict(mount.__grains__, {'kernel': ''}):
|
||||||
|
with patch.object(mount, 'fstab', mock_fstab):
|
||||||
|
with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- mount.rm_fstab,
|
||||||
|
- config=None)
|
||||||
|
+ m_open.side_effect = IOError(13, 'Permission denied:', '/file')
|
||||||
|
+ self.assertRaises(CommandExecutionError,
|
||||||
|
+ mount.rm_fstab,
|
||||||
|
+ 'name', 'device')
|
||||||
|
|
||||||
|
def test_set_fstab(self):
|
||||||
|
'''
|
||||||
|
@@ -180,11 +180,7 @@ class MountTestCase(TestCase):
|
||||||
|
|
||||||
|
mock = MagicMock(return_value={'name': 'name'})
|
||||||
|
with patch.object(mount, 'fstab', mock):
|
||||||
|
- with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- mount.rm_automaster,
|
||||||
|
- 'name', 'device')
|
||||||
|
+ self.assertTrue(mount.rm_automaster('name', 'device'))
|
||||||
|
|
||||||
|
def test_set_automaster(self):
|
||||||
|
'''
|
||||||
|
diff --git a/tests/unit/modules/portage_config.py b/tests/unit/modules/portage_config.py
|
||||||
|
index 8da1ebe..6275442 100644
|
||||||
|
--- a/tests/unit/modules/portage_config.py
|
||||||
|
+++ b/tests/unit/modules/portage_config.py
|
||||||
|
@@ -11,7 +11,7 @@ from __future__ import absolute_import
|
||||||
|
# Import Salt Testing libs
|
||||||
|
from salttesting import skipIf, TestCase
|
||||||
|
from salttesting.helpers import ensure_in_syspath
|
||||||
|
-from salttesting.mock import NO_MOCK, NO_MOCK_REASON
|
||||||
|
+from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock
|
||||||
|
ensure_in_syspath('../../')
|
||||||
|
|
||||||
|
# Import salt libs
|
||||||
|
@@ -20,6 +20,10 @@ from salt.modules import portage_config
|
||||||
|
|
||||||
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class PortageConfigTestCase(TestCase):
|
||||||
|
+ class DummyAtom(object):
|
||||||
|
+ def __init__(self, atom):
|
||||||
|
+ self.cp, self.repo = atom.split("::") if "::" in atom else (atom, None)
|
||||||
|
+
|
||||||
|
def test_get_config_file_wildcards(self):
|
||||||
|
pairs = [
|
||||||
|
('*/*::repo', '/etc/portage/package.mask/repo'),
|
||||||
|
@@ -29,7 +33,11 @@ class PortageConfigTestCase(TestCase):
|
||||||
|
('cat/pkg::repo', '/etc/portage/package.mask/cat/pkg'),
|
||||||
|
]
|
||||||
|
|
||||||
|
+ portage_config.portage = MagicMock()
|
||||||
|
for (atom, expected) in pairs:
|
||||||
|
+ dummy_atom = self.DummyAtom(atom)
|
||||||
|
+ portage_config.portage.dep.Atom = MagicMock(return_value=dummy_atom)
|
||||||
|
+ portage_config._p_to_cp = MagicMock(return_value=dummy_atom.cp)
|
||||||
|
self.assertEqual(portage_config._get_config_file('mask', atom), expected)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
diff --git a/tests/unit/modules/puppet_test.py b/tests/unit/modules/puppet_test.py
|
||||||
|
index 02bc2e1..2cdd696 100644
|
||||||
|
--- a/tests/unit/modules/puppet_test.py
|
||||||
|
+++ b/tests/unit/modules/puppet_test.py
|
||||||
|
@@ -85,10 +85,12 @@ class PuppetTestCase(TestCase):
|
||||||
|
with patch('salt.utils.fopen', mock_open()):
|
||||||
|
self.assertTrue(puppet.disable())
|
||||||
|
|
||||||
|
- with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- puppet.disable)
|
||||||
|
+ try:
|
||||||
|
+ with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
+ m_open.side_effect = IOError(13, 'Permission denied:', '/file')
|
||||||
|
+ self.assertRaises(CommandExecutionError, puppet.disable)
|
||||||
|
+ except StopIteration:
|
||||||
|
+ pass
|
||||||
|
|
||||||
|
def test_status(self):
|
||||||
|
'''
|
||||||
|
@@ -145,9 +147,8 @@ class PuppetTestCase(TestCase):
|
||||||
|
self.assertDictEqual(puppet.summary(), {'resources': 1})
|
||||||
|
|
||||||
|
with patch('salt.utils.fopen', mock_open()) as m_open:
|
||||||
|
- helper_open = m_open()
|
||||||
|
- helper_open.write.assertRaises(CommandExecutionError,
|
||||||
|
- puppet.summary)
|
||||||
|
+ m_open.side_effect = IOError(13, 'Permission denied:', '/file')
|
||||||
|
+ self.assertRaises(CommandExecutionError, puppet.summary)
|
||||||
|
|
||||||
|
def test_plugin_sync(self):
|
||||||
|
'''
|
||||||
|
diff --git a/tests/unit/modules/useradd_test.py b/tests/unit/modules/useradd_test.py
|
||||||
|
index 7e646b6..cc9e610 100644
|
||||||
|
--- a/tests/unit/modules/useradd_test.py
|
||||||
|
+++ b/tests/unit/modules/useradd_test.py
|
||||||
|
@@ -326,7 +326,7 @@ class UserAddTestCase(TestCase):
|
||||||
|
'''
|
||||||
|
Test the user information
|
||||||
|
'''
|
||||||
|
- self.assertEqual(useradd.info('salt'), {})
|
||||||
|
+ self.assertEqual(useradd.info('username-that-doesnt-exist'), {})
|
||||||
|
|
||||||
|
mock = MagicMock(return_value=pwd.struct_passwd(('_TEST_GROUP',
|
||||||
|
'*',
|
||||||
|
@@ -336,9 +336,7 @@ class UserAddTestCase(TestCase):
|
||||||
|
'/var/virusmails',
|
||||||
|
'/usr/bin/false')))
|
||||||
|
with patch.object(pwd, 'getpwnam', mock):
|
||||||
|
- mock = MagicMock(return_value='Group Name')
|
||||||
|
- with patch.object(useradd, 'list_groups', mock):
|
||||||
|
- self.assertEqual(useradd.info('salt')['name'], '_TEST_GROUP')
|
||||||
|
+ self.assertEqual(useradd.info('username-that-doesnt-exist')['name'], '_TEST_GROUP')
|
||||||
|
|
||||||
|
# 'list_groups' function tests: 1
|
||||||
|
|
||||||
|
diff --git a/tests/unit/pyobjects_test.py b/tests/unit/pyobjects_test.py
|
||||||
|
index f1c3e29..3eb4bd5 100644
|
||||||
|
--- a/tests/unit/pyobjects_test.py
|
||||||
|
+++ b/tests/unit/pyobjects_test.py
|
||||||
|
@@ -54,10 +54,18 @@ include('http')
|
||||||
|
|
||||||
|
extend_template = '''#!pyobjects
|
||||||
|
include('http')
|
||||||
|
+
|
||||||
|
+from salt.utils.pyobjects import StateFactory
|
||||||
|
+Service = StateFactory('service')
|
||||||
|
+
|
||||||
|
Service.running(extend('apache'), watch=[{'file': '/etc/file'}])
|
||||||
|
'''
|
||||||
|
|
||||||
|
map_template = '''#!pyobjects
|
||||||
|
+from salt.utils.pyobjects import StateFactory
|
||||||
|
+Service = StateFactory('service')
|
||||||
|
+
|
||||||
|
+
|
||||||
|
class Samba(Map):
|
||||||
|
__merge__ = 'samba:lookup'
|
||||||
|
|
||||||
|
@@ -127,6 +135,9 @@ from salt://password.sls import password
|
||||||
|
'''
|
||||||
|
|
||||||
|
requisite_implicit_list_template = '''#!pyobjects
|
||||||
|
+from salt.utils.pyobjects import StateFactory
|
||||||
|
+Service = StateFactory('service')
|
||||||
|
+
|
||||||
|
with Pkg.installed("pkg"):
|
||||||
|
Service.running("service", watch=File("file"), require=Cmd("cmd"))
|
||||||
|
'''
|
||||||
|
diff --git a/tests/unit/states/boto_cloudtrail_test.py b/tests/unit/states/boto_cloudtrail_test.py
|
||||||
|
index 48fbd32..9e6dd95 100644
|
||||||
|
--- a/tests/unit/states/boto_cloudtrail_test.py
|
||||||
|
+++ b/tests/unit/states/boto_cloudtrail_test.py
|
||||||
|
@@ -104,6 +104,11 @@ if _has_required_boto():
|
||||||
|
StopLoggingTime=None)
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoCloudTrailStateTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -124,11 +129,6 @@ class BotoCloudTrailStateTestCaseBase(TestCase):
|
||||||
|
session_instance.client.return_value = self.conn
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoCloudTrailTestCase(BotoCloudTrailStateTestCaseBase, BotoCloudTrailTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_cloudtrail state.module
|
||||||
|
diff --git a/tests/unit/states/boto_iot_test.py b/tests/unit/states/boto_iot_test.py
|
||||||
|
index 8c2549d..81d68c8 100644
|
||||||
|
--- a/tests/unit/states/boto_iot_test.py
|
||||||
|
+++ b/tests/unit/states/boto_iot_test.py
|
||||||
|
@@ -103,6 +103,11 @@ if _has_required_boto():
|
||||||
|
principal = 'arn:aws:iot:us-east-1:1234:cert/21fc104aaaf6043f5756c1b57bda84ea8395904c43f28517799b19e4c42514'
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoIoTStateTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -123,11 +128,6 @@ class BotoIoTStateTestCaseBase(TestCase):
|
||||||
|
session_instance.client.return_value = self.conn
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoIoTPolicyTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_iot state.module
|
||||||
|
diff --git a/tests/unit/states/boto_lambda_test.py b/tests/unit/states/boto_lambda_test.py
|
||||||
|
index 4557aed..7b02391 100644
|
||||||
|
--- a/tests/unit/states/boto_lambda_test.py
|
||||||
|
+++ b/tests/unit/states/boto_lambda_test.py
|
||||||
|
@@ -101,6 +101,11 @@ def _has_required_boto():
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoLambdaStateTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -121,11 +126,6 @@ class BotoLambdaStateTestCaseBase(TestCase):
|
||||||
|
session_instance.client.return_value = self.conn
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoLambdaFunctionTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_lambda state.module
|
||||||
|
diff --git a/tests/unit/states/boto_s3_bucket_test.py b/tests/unit/states/boto_s3_bucket_test.py
|
||||||
|
index 4049e9a..03c406f 100644
|
||||||
|
--- a/tests/unit/states/boto_s3_bucket_test.py
|
||||||
|
+++ b/tests/unit/states/boto_s3_bucket_test.py
|
||||||
|
@@ -277,6 +277,11 @@ if _has_required_boto():
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
+@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
+ ' or equal to version {0}'
|
||||||
|
+ .format(required_boto3_version))
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoS3BucketStateTestCaseBase(TestCase):
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
@@ -297,11 +302,6 @@ class BotoS3BucketStateTestCaseBase(TestCase):
|
||||||
|
session_instance.client.return_value = self.conn
|
||||||
|
|
||||||
|
|
||||||
|
-@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
|
||||||
|
-@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
|
||||||
|
- ' or equal to version {0}'
|
||||||
|
- .format(required_boto3_version))
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
class BotoS3BucketTestCase(BotoS3BucketStateTestCaseBase, BotoS3BucketTestCaseMixin):
|
||||||
|
'''
|
||||||
|
TestCase for salt.modules.boto_s3_bucket state.module
|
||||||
|
diff --git a/tests/unit/states/dockerio.py b/tests/unit/states/dockerio.py
|
||||||
|
deleted file mode 100644
|
||||||
|
index c73b633..0000000
|
||||||
|
--- a/tests/unit/states/dockerio.py
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,112 +0,0 @@
|
||||||
|
-# -*- coding: utf-8 -*-
|
||||||
|
-
|
||||||
|
-# Import Python libs
|
||||||
|
-from __future__ import absolute_import
|
||||||
|
-from contextlib import contextmanager
|
||||||
|
-
|
||||||
|
-# Import Salt Testing libs
|
||||||
|
-from salttesting import skipIf, TestCase
|
||||||
|
-from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-@contextmanager
|
||||||
|
-def provision_state(module, fixture):
|
||||||
|
- previous_dict = getattr(module, '__salt__', {}).copy()
|
||||||
|
- try:
|
||||||
|
- module.__dict__.setdefault('__salt__', {}).update(fixture)
|
||||||
|
- yield
|
||||||
|
- finally:
|
||||||
|
- setattr(module, '__salt__', previous_dict)
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
-class DockerStateTestCase(TestCase):
|
||||||
|
- def test_docker_run_success(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
- 'docker.run_all': MagicMock(
|
||||||
|
- return_value={'stdout': '.\n..\n',
|
||||||
|
- 'stderr': '',
|
||||||
|
- 'status': True,
|
||||||
|
- 'comment': 'Success',
|
||||||
|
- 'retcode': 0})}
|
||||||
|
-
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu')
|
||||||
|
-
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': True,
|
||||||
|
- 'comment': 'Success',
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
- def test_docker_run_failure(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
- 'docker.run_all': MagicMock(
|
||||||
|
- return_value={'stdout': '',
|
||||||
|
- 'stderr': 'Error',
|
||||||
|
- 'status': False,
|
||||||
|
- 'comment': 'Failure',
|
||||||
|
- 'retcode': 1})}
|
||||||
|
-
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu')
|
||||||
|
-
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': False,
|
||||||
|
- 'comment': 'Failure',
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
- def test_docker_run_onlyif(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=1),
|
||||||
|
- 'docker.run_all': None}
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
- onlyif='ls -l')
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': True,
|
||||||
|
- 'comment': 'onlyif execution failed',
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
- def test_docker_run_unless(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
- 'docker.run_all': None}
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
- unless='ls -l')
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': True,
|
||||||
|
- 'comment': 'unless execution succeeded',
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
- def test_docker_run_docked_onlyif(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=1),
|
||||||
|
- 'docker.run_all': None}
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
- docked_onlyif='ls -l')
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': True,
|
||||||
|
- 'comment': 'docked_onlyif execution failed',
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
- def test_docker_run_docked_unless(self):
|
||||||
|
- from salt.states import dockerio
|
||||||
|
- salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
- 'docker.run_all': None}
|
||||||
|
- with provision_state(dockerio, salt_fixture):
|
||||||
|
- result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
- docked_unless='ls -l')
|
||||||
|
- self.assertEqual(result, {'name': 'ls /',
|
||||||
|
- 'result': True,
|
||||||
|
- 'comment': ('docked_unless execution'
|
||||||
|
- ' succeeded'),
|
||||||
|
- 'changes': {}})
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-if __name__ == '__main__':
|
||||||
|
- from integration import run_tests
|
||||||
|
- run_tests(DockerStateTestCase, needs_daemon=False)
|
||||||
|
diff --git a/tests/unit/states/dockerio_test.py b/tests/unit/states/dockerio_test.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..54f51be
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/unit/states/dockerio_test.py
|
||||||
|
@@ -0,0 +1,113 @@
|
||||||
|
+# -*- coding: utf-8 -*-
|
||||||
|
+
|
||||||
|
+# Import Python libs
|
||||||
|
+from __future__ import absolute_import
|
||||||
|
+from contextlib import contextmanager
|
||||||
|
+
|
||||||
|
+# Import Salt Testing libs
|
||||||
|
+from salttesting import skipIf, TestCase
|
||||||
|
+from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@contextmanager
|
||||||
|
+def provision_state(module, fixture):
|
||||||
|
+ previous_dict = getattr(module, '__salt__', {}).copy()
|
||||||
|
+ try:
|
||||||
|
+ module.__dict__.setdefault('__salt__', {}).update(fixture)
|
||||||
|
+ yield
|
||||||
|
+ finally:
|
||||||
|
+ setattr(module, '__salt__', previous_dict)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
+@skipIf(True, 'Skipped: This module has been deprecated.')
|
||||||
|
+class DockerStateTestCase(TestCase):
|
||||||
|
+ def test_docker_run_success(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
+ 'docker.run_all': MagicMock(
|
||||||
|
+ return_value={'stdout': '.\n..\n',
|
||||||
|
+ 'stderr': '',
|
||||||
|
+ 'status': True,
|
||||||
|
+ 'comment': 'Success',
|
||||||
|
+ 'retcode': 0})}
|
||||||
|
+
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu')
|
||||||
|
+
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': True,
|
||||||
|
+ 'comment': 'Success',
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+ def test_docker_run_failure(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
+ 'docker.run_all': MagicMock(
|
||||||
|
+ return_value={'stdout': '',
|
||||||
|
+ 'stderr': 'Error',
|
||||||
|
+ 'status': False,
|
||||||
|
+ 'comment': 'Failure',
|
||||||
|
+ 'retcode': 1})}
|
||||||
|
+
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu')
|
||||||
|
+
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': False,
|
||||||
|
+ 'comment': 'Failure',
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+ def test_docker_run_onlyif(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=1),
|
||||||
|
+ 'docker.run_all': None}
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
+ onlyif='ls -l')
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': True,
|
||||||
|
+ 'comment': 'onlyif execution failed',
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+ def test_docker_run_unless(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
+ 'docker.run_all': None}
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
+ unless='ls -l')
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': True,
|
||||||
|
+ 'comment': 'unless execution succeeded',
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+ def test_docker_run_docked_onlyif(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=1),
|
||||||
|
+ 'docker.run_all': None}
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
+ docked_onlyif='ls -l')
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': True,
|
||||||
|
+ 'comment': 'docked_onlyif execution failed',
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+ def test_docker_run_docked_unless(self):
|
||||||
|
+ from salt.states import dockerio
|
||||||
|
+ salt_fixture = {'docker.retcode': MagicMock(return_value=0),
|
||||||
|
+ 'docker.run_all': None}
|
||||||
|
+ with provision_state(dockerio, salt_fixture):
|
||||||
|
+ result = dockerio.run('ls /', 'ubuntu',
|
||||||
|
+ docked_unless='ls -l')
|
||||||
|
+ self.assertEqual(result, {'name': 'ls /',
|
||||||
|
+ 'result': True,
|
||||||
|
+ 'comment': ('docked_unless execution'
|
||||||
|
+ ' succeeded'),
|
||||||
|
+ 'changes': {}})
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+if __name__ == '__main__':
|
||||||
|
+ from integration import run_tests
|
||||||
|
+ run_tests(DockerStateTestCase, needs_daemon=False)
|
||||||
|
diff --git a/tests/unit/utils/network.py b/tests/unit/utils/network.py
|
||||||
|
index 89db848..72ca857 100644
|
||||||
|
--- a/tests/unit/utils/network.py
|
||||||
|
+++ b/tests/unit/utils/network.py
|
||||||
|
@@ -151,15 +151,16 @@ class NetworkTestCase(TestCase):
|
||||||
|
self.assertEqual(interfaces,
|
||||||
|
{'ilbext0': {'inet': [{'address': '10.10.11.11',
|
||||||
|
'broadcast': '10.10.11.31',
|
||||||
|
+ 'netmask': '255.255.255.224'},
|
||||||
|
+ {'address': '10.10.11.12',
|
||||||
|
+ 'broadcast': '10.10.11.31',
|
||||||
|
'netmask': '255.255.255.224'}],
|
||||||
|
- 'inet6': [{'address': '::',
|
||||||
|
- 'prefixlen': '0'}],
|
||||||
|
+ 'inet6': [],
|
||||||
|
'up': True},
|
||||||
|
'ilbint0': {'inet': [{'address': '10.6.0.11',
|
||||||
|
'broadcast': '10.6.0.255',
|
||||||
|
'netmask': '255.255.255.0'}],
|
||||||
|
- 'inet6': [{'address': '::',
|
||||||
|
- 'prefixlen': '0'}],
|
||||||
|
+ 'inet6': [],
|
||||||
|
'up': True},
|
||||||
|
'lo0': {'inet': [{'address': '127.0.0.1',
|
||||||
|
'netmask': '255.0.0.0'}],
|
||||||
|
@@ -174,8 +175,7 @@ class NetworkTestCase(TestCase):
|
||||||
|
'up': True},
|
||||||
|
'vpn0': {'inet': [{'address': '10.6.0.14',
|
||||||
|
'netmask': '255.0.0.0'}],
|
||||||
|
- 'inet6': [{'address': '::',
|
||||||
|
- 'prefixlen': '0'}],
|
||||||
|
+ 'inet6': [],
|
||||||
|
'up': True}}
|
||||||
|
)
|
||||||
|
|
||||||
|
diff --git a/tests/unit/utils/utils_test.py b/tests/unit/utils/utils_test.py
|
||||||
|
index 261af69..11f0baf 100644
|
||||||
|
--- a/tests/unit/utils/utils_test.py
|
||||||
|
+++ b/tests/unit/utils/utils_test.py
|
||||||
|
@@ -527,14 +527,9 @@ class UtilsTestCase(TestCase):
|
||||||
|
ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013')
|
||||||
|
expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15)
|
||||||
|
self.assertEqual(ret, expected_ret)
|
||||||
|
- except ImportError:
|
||||||
|
- try:
|
||||||
|
- ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013')
|
||||||
|
- expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15)
|
||||||
|
- self.assertEqual(ret, expected_ret)
|
||||||
|
- except RuntimeError:
|
||||||
|
- # Unparseable without timelib installed
|
||||||
|
- self.skipTest('\'timelib\' is not installed')
|
||||||
|
+ except RuntimeError:
|
||||||
|
+ # Unparseable without timelib installed
|
||||||
|
+ self.skipTest('\'timelib\' is not installed')
|
||||||
|
|
||||||
|
@skipIf(not HAS_TIMELIB, '\'timelib\' is not installed')
|
||||||
|
def test_date_format(self):
|
||||||
|
--
|
||||||
|
2.8.2
|
||||||
|
|
113
0019-Fix-snapper_test-for-python26.patch
Normal file
113
0019-Fix-snapper_test-for-python26.patch
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
From df1f88c51a40e69935830d1664a46dadf514dc69 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Justin Anderson <janderson@saltstack.com>
|
||||||
|
Date: Tue, 23 Aug 2016 15:02:31 -0600
|
||||||
|
Subject: [PATCH 19/19] 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 <psuarezhernandez@suse.de>
|
||||||
|
'''
|
||||||
|
|
||||||
|
+# 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.8.2
|
||||||
|
|
107
0020-Integration-tests-fixes-for-2016.3.2.patch
Normal file
107
0020-Integration-tests-fixes-for-2016.3.2.patch
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
From 65dba3e9ab088087943f67367dc27fce00e4cd92 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Tue, 13 Sep 2016 11:05:35 +0100
|
||||||
|
Subject: [PATCH 20/20] Integration tests fixes for 2016.3.2
|
||||||
|
|
||||||
|
* Fix PillarModuleTest::test_pillar_items: 'info' does not exist in pillar
|
||||||
|
* Fixing integration tests if azure is not present
|
||||||
|
* Fixing integration tests failures if 'git' command is missing
|
||||||
|
Skip git state integration tests if 'git' does not exists
|
||||||
|
Prevent OSError if 'git' command not found during _git_version()
|
||||||
|
---
|
||||||
|
tests/integration/cloud/providers/msazure.py | 17 +++++++++--------
|
||||||
|
tests/integration/modules/git.py | 15 +++++++++------
|
||||||
|
tests/integration/modules/pillar.py | 1 -
|
||||||
|
tests/integration/states/git.py | 2 +-
|
||||||
|
4 files changed, 19 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/tests/integration/cloud/providers/msazure.py b/tests/integration/cloud/providers/msazure.py
|
||||||
|
index c4934e4..4e53add 100644
|
||||||
|
--- a/tests/integration/cloud/providers/msazure.py
|
||||||
|
+++ b/tests/integration/cloud/providers/msazure.py
|
||||||
|
@@ -53,14 +53,15 @@ def __has_required_azure():
|
||||||
|
'''
|
||||||
|
Returns True/False if the required version of the Azure SDK is installed.
|
||||||
|
'''
|
||||||
|
- if hasattr(azure, '__version__'):
|
||||||
|
- version = LooseVersion(azure.__version__)
|
||||||
|
- else:
|
||||||
|
- version = LooseVersion(azure.common.__version__)
|
||||||
|
- if HAS_AZURE is True and REQUIRED_AZURE <= version:
|
||||||
|
- return True
|
||||||
|
- else:
|
||||||
|
- return False
|
||||||
|
+ if HAS_AZURE:
|
||||||
|
+ if hasattr(azure, '__version__'):
|
||||||
|
+ version = LooseVersion(azure.__version__)
|
||||||
|
+ else:
|
||||||
|
+ version = LooseVersion(azure.common.__version__)
|
||||||
|
+
|
||||||
|
+ if REQUIRED_AZURE <= version:
|
||||||
|
+ return True
|
||||||
|
+ return False
|
||||||
|
|
||||||
|
|
||||||
|
@skipIf(HAS_AZURE is False, 'These tests require the Azure Python SDK to be installed.')
|
||||||
|
diff --git a/tests/integration/modules/git.py b/tests/integration/modules/git.py
|
||||||
|
index 7c40664..f5c15f3 100644
|
||||||
|
--- a/tests/integration/modules/git.py
|
||||||
|
+++ b/tests/integration/modules/git.py
|
||||||
|
@@ -36,12 +36,15 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _git_version():
|
||||||
|
- git_version = subprocess.Popen(
|
||||||
|
- ['git', '--version'],
|
||||||
|
- shell=False,
|
||||||
|
- close_fds=True,
|
||||||
|
- stdout=subprocess.PIPE,
|
||||||
|
- stderr=subprocess.PIPE).communicate()[0]
|
||||||
|
+ try:
|
||||||
|
+ git_version = subprocess.Popen(
|
||||||
|
+ ['git', '--version'],
|
||||||
|
+ shell=False,
|
||||||
|
+ close_fds=True,
|
||||||
|
+ stdout=subprocess.PIPE,
|
||||||
|
+ stderr=subprocess.PIPE).communicate()[0]
|
||||||
|
+ except OSError:
|
||||||
|
+ return False
|
||||||
|
if not git_version:
|
||||||
|
log.debug('Git not installed')
|
||||||
|
return False
|
||||||
|
diff --git a/tests/integration/modules/pillar.py b/tests/integration/modules/pillar.py
|
||||||
|
index b081f76..3d55b4c 100644
|
||||||
|
--- a/tests/integration/modules/pillar.py
|
||||||
|
+++ b/tests/integration/modules/pillar.py
|
||||||
|
@@ -119,7 +119,6 @@ class PillarModuleTest(integration.ModuleCase):
|
||||||
|
from pillar.items
|
||||||
|
'''
|
||||||
|
get_items = self.run_function('pillar.items')
|
||||||
|
- self.assertDictContainsSubset({'info': 'bar'}, get_items)
|
||||||
|
self.assertDictContainsSubset({'monty': 'python'}, get_items)
|
||||||
|
self.assertDictContainsSubset(
|
||||||
|
{'knights': ['Lancelot', 'Galahad', 'Bedevere', 'Robin']},
|
||||||
|
diff --git a/tests/integration/states/git.py b/tests/integration/states/git.py
|
||||||
|
index a78b271..87681ba 100644
|
||||||
|
--- a/tests/integration/states/git.py
|
||||||
|
+++ b/tests/integration/states/git.py
|
||||||
|
@@ -20,6 +20,7 @@ import integration
|
||||||
|
import salt.utils
|
||||||
|
|
||||||
|
|
||||||
|
+@skip_if_binaries_missing('git')
|
||||||
|
class GitTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
|
||||||
|
'''
|
||||||
|
Validate the git state
|
||||||
|
@@ -254,7 +255,6 @@ class GitTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(name, ignore_errors=True)
|
||||||
|
|
||||||
|
- @skip_if_binaries_missing('git')
|
||||||
|
def test_config_set_value_with_space_character(self):
|
||||||
|
'''
|
||||||
|
git.config
|
||||||
|
--
|
||||||
|
2.8.2
|
||||||
|
|
31
0021-Fix-pkg.upgrade-for-zypper.patch
Normal file
31
0021-Fix-pkg.upgrade-for-zypper.patch
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
From 2cac0c3839af12b0a474f4cb0c0854995cd8dc2a Mon Sep 17 00:00:00 2001
|
||||||
|
From: "C. R. Oldham" <cro@ncbt.org>
|
||||||
|
Date: Wed, 21 Sep 2016 20:05:33 -0600
|
||||||
|
Subject: [PATCH 21/21] Fix pkg.upgrade for zypper
|
||||||
|
|
||||||
|
---
|
||||||
|
salt/modules/zypper.py | 7 ++++---
|
||||||
|
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
|
||||||
|
index 4bb9a09..21b87b0 100644
|
||||||
|
--- a/salt/modules/zypper.py
|
||||||
|
+++ b/salt/modules/zypper.py
|
||||||
|
@@ -1079,10 +1079,11 @@ def upgrade(refresh=True, skip_verify=False):
|
||||||
|
refresh_db()
|
||||||
|
old = list_pkgs()
|
||||||
|
|
||||||
|
- to_append = ''
|
||||||
|
if skip_verify:
|
||||||
|
- to_append = '--no-gpg-checks'
|
||||||
|
- __zypper__.noraise.call('update', '--auto-agree-with-licenses', to_append)
|
||||||
|
+ __zypper__.noraise.call('update', '--auto-agree-with-licenses', '--no-gpg-checks')
|
||||||
|
+ else:
|
||||||
|
+ __zypper__.noraise.call('update', '--auto-agree-with-licenses')
|
||||||
|
+
|
||||||
|
if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES:
|
||||||
|
ret['result'] = False
|
||||||
|
ret['comment'] = (__zypper__.stdout() + os.linesep + __zypper__.stderr()).strip()
|
||||||
|
--
|
||||||
|
2.10.0
|
||||||
|
|
3
html.tar.bz2
Normal file
3
html.tar.bz2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0899b89ef230b42097f7c147a9babb30d65eac7968bd05318eac42ec9d8a7ec9
|
||||||
|
size 5486695
|
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:b2ecce7bf562cfcd6586d66ade278f268bb89023f0fa0accaa55f90b8a668ef5
|
|
||||||
size 6982904
|
|
3
salt-2016.3.2.tar.gz
Normal file
3
salt-2016.3.2.tar.gz
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6a84b724d02b0dba438dea57650724064675d82620e66749bd2fe8f21da71be0
|
||||||
|
size 8014793
|
288
salt.changes
288
salt.changes
@ -1,3 +1,291 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Sep 28 12:35:32 UTC 2016 - tampakrap@opensuse.org
|
||||||
|
|
||||||
|
- Rename susemanager plugin to zyppnotify, as it is not SUSE Manager specific
|
||||||
|
- Remove the subpackage and put the plugin back to the main package
|
||||||
|
according to upstream
|
||||||
|
Add:
|
||||||
|
* 0007-Add-zypp-notify-plugin.patch
|
||||||
|
Remove:
|
||||||
|
* 0007-Add-SUSE-Manager-plugin.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Sep 28 11:40:36 UTC 2016 - tampakrap@opensuse.org
|
||||||
|
|
||||||
|
- Add upstream patch to fix pkg.upgrade for zypper
|
||||||
|
Add:
|
||||||
|
* 0021-Fix-pkg.upgrade-for-zypper.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Sep 27 23:55:03 UTC 2016 - mrueckert@suse.de
|
||||||
|
|
||||||
|
- splitting out the susemanager integration plugins into their own
|
||||||
|
subpackages. ATM this only contains the zypp plugin to tell
|
||||||
|
susemanager about manually installed packages.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Sep 13 15:07:44 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Unit and integration tests fixes for 2016.3.2
|
||||||
|
Add:
|
||||||
|
* 0018-Unit-tests-fixes-for-2016.3.2.patch
|
||||||
|
* 0019-Fix-snapper_test-for-python26.patch
|
||||||
|
* 0020-Integration-tests-fixes-for-2016.3.2.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Sep 2 09:50:57 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Prevent pkg.install failure for expired keys (bsc#996455)
|
||||||
|
Add:
|
||||||
|
* 0017-Check-for-single-quote-before-splitting-on-single-qu.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Aug 30 12:24:45 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Required D-Bus and generating machine ID where it is missing
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Aug 26 13:11:58 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Fix sphinx crashes when documentation is being generated
|
||||||
|
Add script for documentation update.
|
||||||
|
|
||||||
|
Add:
|
||||||
|
* 0016-Improve-Mock-to-be-flexible-and-able-to-mock-methods.patch
|
||||||
|
* update-documentation.sh
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Aug 16 12:01:59 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Fix pkg.installed refresh repo failure (bsc#993549)
|
||||||
|
Fix salt.states.pkgrepo.management no change failure (bsc#990440)
|
||||||
|
Add:
|
||||||
|
* 0014-Add-ignore_repo_failure-option-to-suppress-zypper-s-.patch
|
||||||
|
* 0015-Remove-zypper-s-raise-exception-if-mod_repo-has-no-a.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Aug 9 13:40:37 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Deprecate status.uptime one version later
|
||||||
|
Add:
|
||||||
|
* 0013-Deprecate-status.uptime-one-version-later.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Aug 8 16:25:04 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Fix for 0008-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
after upstream merging conflict. Prevent zypper test failure.
|
||||||
|
Update:
|
||||||
|
* 0008-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu Aug 4 14:56:42 UTC 2016 - mihai.dinca@suse.com
|
||||||
|
|
||||||
|
- Update to v2016.3.2
|
||||||
|
see https://docs.saltstack.com/en/latest/topics/releases/2016.3.2.html
|
||||||
|
- Removed Patches, applied upstream
|
||||||
|
* 0010-Getting-the-os-grain-from-CPE_NAME-inside-etc-os-rel.patch
|
||||||
|
* 0011-snapper-execution-module.patch
|
||||||
|
* 0012-Force-minion-exit-on-mis-configuration-read.patch
|
||||||
|
* 0013-fix-salt-summary-to-count-not-responding-minions-cor.patch
|
||||||
|
* 0014-Move-log-message-from-INFO-to-DEBUG.patch
|
||||||
|
* 0016-Run-salt-api-as-user-salt-bsc-990029.patch
|
||||||
|
* 0017-fix-beacon-list-to-include-all-beacons-being-process.patch
|
||||||
|
- Added Patches back
|
||||||
|
* 0010-snapper-execution-module.patch
|
||||||
|
* 0011-fix-salt-summary-to-count-not-responding-minions-cor.patch
|
||||||
|
* 0012-Run-salt-api-as-user-salt-bsc-990029.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Aug 3 14:50:35 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Fix beacon list to include all beacons being process
|
||||||
|
Add:
|
||||||
|
* 0017-fix-beacon-list-to-include-all-beacons-being-process.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Jul 29 11:48:37 UTC 2016 - kkaempf@suse.com
|
||||||
|
|
||||||
|
- Redo patches, sync with github.com/openSUSE/salt
|
||||||
|
|
||||||
|
- Renamed patches
|
||||||
|
* 0016-Getting-the-os-grain-from-CPE_NAME-inside-etc-os-rel.patch
|
||||||
|
-> 0010-Getting-the-os-grain-from-CPE_NAME-inside-etc-os-rel.patch
|
||||||
|
* 0017-snapper-execution-module.patch
|
||||||
|
-> 0011-snapper-execution-module.patch
|
||||||
|
* 0018-Force-minion-exit-on-mis-configuration-read.patch
|
||||||
|
-> 0012-Force-minion-exit-on-mis-configuration-read.patch
|
||||||
|
|
||||||
|
- Added back (fix upstream merge problems):
|
||||||
|
* 0008-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
* 0009-unit-tests-for-rpm.checksum-and-zypper.download.patch
|
||||||
|
|
||||||
|
- fix salt --summary to count not responding minions correctly
|
||||||
|
(bsc#972311)
|
||||||
|
* 0013-fix-salt-summary-to-count-not-responding-minions-cor.patch
|
||||||
|
|
||||||
|
- Move log message from INFO to DEBUG (bsc#985661)
|
||||||
|
* 0014-Move-log-message-from-INFO-to-DEBUG.patch
|
||||||
|
|
||||||
|
- pkg.list_products on "registerrelease" and "productline"
|
||||||
|
returns boolean.False if empty (bsc#989193)
|
||||||
|
* 0015-Bugfix-return-boolean-only-for-isbase-and-installed-.patch
|
||||||
|
|
||||||
|
- Run salt-api as user salt like the master (bsc#990029)
|
||||||
|
* 0016-Run-salt-api-as-user-salt-bsc-990029.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Jul 22 15:16:16 UTC 2016 - darin@darins.net
|
||||||
|
|
||||||
|
- Update to v2016.3.1
|
||||||
|
see https://docs.saltstack.com/en/latest/topics/releases/2016.3.1.html
|
||||||
|
- Removed Patches, applied upstream
|
||||||
|
* 0008-Prevent-several-minion-processes-on-the-same-machine.patch
|
||||||
|
* 0009-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
* 0010-unit-tests-for-rpm.checksum-and-zypper.download.patch
|
||||||
|
* 0011-jobs.exit_success-allow-to-check-if-a-job-has-execut.patch
|
||||||
|
* 0012-Fix-pkgrepo.managed-gpgkey-argument-bsc-979448.patch
|
||||||
|
* 0013-fix-groupadd-module-for-sles11-systems.patch
|
||||||
|
* 0014-Fix-crashing-Maintenence-process.patch
|
||||||
|
* 0015-Insert-no-refresh-before-install-in-Zypper.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Jul 19 15:03:10 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Bugfix: Exit on configuration read (bsc#985667)
|
||||||
|
Add:
|
||||||
|
* 0018-Force-minion-exit-on-mis-configuration-read.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Jul 18 08:32:04 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Backport: Snapper module for Salt.
|
||||||
|
Add:
|
||||||
|
* 0017-snapper-execution-module.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Jun 22 07:56:44 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Bugfix: Fixed behavior for SUSE OS grains (bsc#970669)
|
||||||
|
Bugfix: Salt os_family does not detect SLES for SAP (bsc#983017)
|
||||||
|
Add:
|
||||||
|
* 0016-Getting-the-os-grain-from-CPE_NAME-inside-etc-os-rel.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Jun 20 11:26:29 UTC 2016 - thipp@suse.com
|
||||||
|
|
||||||
|
- Insert --no-refresh before install in Zypper
|
||||||
|
Add:
|
||||||
|
* 0015-Insert-no-refresh-before-install-in-Zypper.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Jun 15 11:38:26 UTC 2016 - kkaempf@suse.com
|
||||||
|
|
||||||
|
- Update to v2016.3.0
|
||||||
|
see https://docs.saltstack.com/en/latest/topics/releases/2016.3.0.html
|
||||||
|
|
||||||
|
* backwards-incompatible changes:
|
||||||
|
- The default path for the extension_modules master config option
|
||||||
|
has been changed.
|
||||||
|
|
||||||
|
- add 0014-Fix-crashing-Maintenence-process.patch
|
||||||
|
see release notes
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Jun 1 09:52:40 UTC 2016 - mihai.dinca@suse.com
|
||||||
|
|
||||||
|
- Fix pkgrepo.managed gpgkey argument doesn't work (bsc#979448)
|
||||||
|
Add:
|
||||||
|
* 0012-Fix-pkgrepo.managed-gpgkey-argument-bsc-979448.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu May 26 11:05:04 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- Package checksum validation for zypper pkg.download
|
||||||
|
Add:
|
||||||
|
* 0009-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
* 0010-unit-tests-for-rpm.checksum-and-zypper.download.patch
|
||||||
|
- Check if a job has executed and returned successfully
|
||||||
|
Add:
|
||||||
|
* 0011-jobs.exit_success-allow-to-check-if-a-job-has-execut.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue May 24 11:06:12 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Prevent several minion processes on the same machine (bsc#975733)
|
||||||
|
|
||||||
|
Add:
|
||||||
|
* 0008-Prevent-several-minion-processes-on-the-same-machine.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon May 23 15:11:42 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Changed Zypper's plugin. Added Unit test and related to that
|
||||||
|
data (bsc#980313).
|
||||||
|
|
||||||
|
Add:
|
||||||
|
* 0006-Create-salt-proxy-instantiated-service-file.patch
|
||||||
|
* 0007-Add-SUSE-Manager-plugin.patch
|
||||||
|
|
||||||
|
Remove:
|
||||||
|
* 0006-Add-SUSE-Manager-plugin.patch
|
||||||
|
* 0007-Create-salt-proxy-instantiated-service-file.patch
|
||||||
|
* 0008-Alter-the-event-name.patch
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon May 23 09:52:29 UTC 2016 - tampakrap@opensuse.org
|
||||||
|
|
||||||
|
- Update to 2015.8.10
|
||||||
|
see https://docs.saltstack.com/en/latest/topics/releases/2015.8.10.html
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri May 20 10:54:42 UTC 2016 - tampakrap@opensuse.org
|
||||||
|
|
||||||
|
- Update to 2015.8.9
|
||||||
|
see https://docs.saltstack.com/en/latest/topics/releases/2015.8.9.html
|
||||||
|
Patches renamed:
|
||||||
|
* 0006-Add-SUSE-Manager-plugin.patch
|
||||||
|
* 0007-Create-salt-proxy-instantiated-service-file.patch
|
||||||
|
* 0008-Alter-the-event-name.patch
|
||||||
|
Patches removed:
|
||||||
|
* 0006-Update-to-2015.8.8.2.patch
|
||||||
|
* 0007-Force-sort-the-RPM-output-to-ensure-latest-version-o.patch
|
||||||
|
* 0008-Cleaner-deprecation-process-with-decorators.patch
|
||||||
|
* 0009-fix-sorting-by-latest-version-when-called-with-an-at.patch
|
||||||
|
* 0010-Prevent-metadata-download-when-getting-installed-pro.patch
|
||||||
|
* 0011-Check-if-EOL-is-available-in-a-particular-product-bs.patch
|
||||||
|
* 0012-Bugfix-salt-key-crashes-if-tries-to-generate-keys-to.patch
|
||||||
|
* 0013-Prevent-crash-if-pygit2-package-is-requesting-re-com.patch
|
||||||
|
* 0014-align-OS-grains-from-older-SLES-with-current-one-326.patch
|
||||||
|
* 0015-Unblock-Zypper.-Modify-environment.patch
|
||||||
|
* 0016-Bugfix-Restore-boolean-values-from-the-repo-configur.patch
|
||||||
|
* 0017-Add-SUSE-Manager-plugin.patch
|
||||||
|
* 0018-Create-salt-proxy-instantiated-service-file.patch
|
||||||
|
* 0019-Alter-the-event-name.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue May 17 10:08:57 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
- Zypper plugin: alter the generated event name on package set
|
||||||
|
change.
|
||||||
|
Add:
|
||||||
|
* 0019-Alter-the-event-name.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu May 12 08:52:26 UTC 2016 - pablo.suarezhernandez@suse.com
|
||||||
|
|
||||||
|
- salt-proxy .service file created (bsc#975306)
|
||||||
|
Add:
|
||||||
|
* 0018-Create-salt-proxy-instantiated-service-file.patch
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu May 12 00:42:34 UTC 2016 - tserong@suse.com
|
||||||
|
|
||||||
|
- Fix file ownership on master keys and cache directories during upgrade
|
||||||
|
(handles upgrading from salt 2014, where the daemon ran as root, to 2015
|
||||||
|
where it runs as the salt user, bsc#979676).
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Wed May 11 07:20:40 UTC 2016 - bmaryniuk@suse.com
|
Wed May 11 07:20:40 UTC 2016 - bmaryniuk@suse.com
|
||||||
|
|
||||||
|
199
salt.spec
199
salt.spec
@ -34,54 +34,83 @@
|
|||||||
%bcond_with test
|
%bcond_with test
|
||||||
%bcond_with raet
|
%bcond_with raet
|
||||||
%bcond_without docs
|
%bcond_without docs
|
||||||
|
%bcond_with builddocs
|
||||||
|
|
||||||
Name: salt
|
Name: salt
|
||||||
Version: 2015.8.8
|
Version: 2016.3.2
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: A parallel remote execution system
|
Summary: A parallel remote execution system
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
Group: System/Monitoring
|
Group: System/Monitoring
|
||||||
Url: http://saltstack.org/
|
Url: http://saltstack.org/
|
||||||
# Git: https://github.com/openSUSE/salt.git
|
# Git: https://github.com/openSUSE/salt.git
|
||||||
Source0: http://pypi.python.org/packages/source/s/%{name}/%{name}-%{version}.tar.gz
|
Source0: https://pypi.io/packages/source/s/%{name}/%{name}-%{version}.tar.gz
|
||||||
Source1: README.SUSE
|
Source1: README.SUSE
|
||||||
Source2: salt-tmpfiles.d
|
Source2: salt-tmpfiles.d
|
||||||
|
Source3: html.tar.bz2
|
||||||
|
Source4: update-documentation.sh
|
||||||
|
|
||||||
# PATCH-FIX-OPENSUSE use-forking-daemon.patch tserong@suse.com -- We don't have python-systemd, so notify can't work
|
# 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: 0001-tserong-suse.com-We-don-t-have-python-systemd-so-not.patch
|
||||||
# PATCH-FIX-OPENSUSE use-salt-user-for-master.patch -- Run salt master as dedicated salt user
|
# PATCH-FIX-OPENSUSE use-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: 0002-Run-salt-master-as-dedicated-salt-user.patch
|
||||||
# PATCH-FIX-OPENSUSE https://github.com/saltstack/salt/pull/30424
|
# 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: 0003-Check-if-byte-strings-are-properly-encoded-in-UTF-8.patch
|
||||||
# PATCH-FIX-OPENSUSE prevent rebuilds in OBS
|
# 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
|
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)
|
# 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
|
Patch5: 0005-Use-SHA256-hash-type-by-default.patch
|
||||||
# PATCH-FIX-UPSTREAM https://docs.saltstack.com/en/latest/topics/releases/2015.8.8.html#salt-2015-8-8-2
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/31798
|
||||||
Patch6: 0006-Update-to-2015.8.8.2.patch
|
# PR already merged. This will be gone in the next version
|
||||||
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/32243
|
Patch6: 0006-Create-salt-proxy-instantiated-service-file.patch
|
||||||
Patch7: 0007-Force-sort-the-RPM-output-to-ensure-latest-version-o.patch
|
|
||||||
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/32068
|
|
||||||
Patch8: 0008-Cleaner-deprecation-process-with-decorators.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32323
|
|
||||||
Patch9: 0009-fix-sorting-by-latest-version-when-called-with-an-at.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32353
|
|
||||||
Patch10: 0010-Prevent-metadata-download-when-getting-installed-pro.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32505
|
|
||||||
Patch11: 0011-Check-if-EOL-is-available-in-a-particular-product-bs.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32436
|
|
||||||
Patch12: 0012-Bugfix-salt-key-crashes-if-tries-to-generate-keys-to.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32652
|
|
||||||
Patch13: 0013-Prevent-crash-if-pygit2-package-is-requesting-re-com.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32649
|
|
||||||
Patch14: 0014-align-OS-grains-from-older-SLES-with-current-one-326.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/32892
|
|
||||||
Patch15: 0015-Unblock-Zypper.-Modify-environment.patch
|
|
||||||
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/33088
|
|
||||||
Patch16: 0016-Bugfix-Restore-boolean-values-from-the-repo-configur.patch
|
|
||||||
# PATCH-FIX-OPENSUSE Generate events from the Salt minion,
|
# PATCH-FIX-OPENSUSE Generate events from the Salt minion,
|
||||||
# if Zypper has been used outside the Salt infrastructure
|
# if Zypper has been used outside the Salt infrastructure
|
||||||
Patch17: 0017-Add-SUSE-Manager-plugin.patch
|
# We do not upstream this because this is for SUSE only (15.08.2016)
|
||||||
|
Patch7: 0007-Add-zypp-notify-plugin.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/33469
|
||||||
|
# PR already merged. This will be gone in the next version
|
||||||
|
Patch8: 0008-checksum-validation-when-zypper-pkg.download.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/33501
|
||||||
|
# PR already merged. This will be gone in the next version
|
||||||
|
Patch9: 0009-unit-tests-for-rpm.checksum-and-zypper.download.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/34599 (backport from dev)
|
||||||
|
# PR already merged. This will be gone in the next version
|
||||||
|
Patch10: 0010-snapper-execution-module.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/34165
|
||||||
|
Patch11: 0011-fix-salt-summary-to-count-not-responding-minions-cor.patch
|
||||||
|
# PATCH-FIX-OPENSUSE
|
||||||
|
# We do not upstream this patch because this is suse custom configuration
|
||||||
|
Patch12: 0012-Run-salt-api-as-user-salt-bsc-990029.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/35196
|
||||||
|
# PR already merged. This will be gone in the next version
|
||||||
|
Patch13: 0013-Deprecate-status.uptime-one-version-later.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/35448
|
||||||
|
Patch14: 0014-Add-ignore_repo_failure-option-to-suppress-zypper-s-.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/35451
|
||||||
|
Patch15: 0015-Remove-zypper-s-raise-exception-if-mod_repo-has-no-a.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/35763
|
||||||
|
Patch16: 0016-Improve-Mock-to-be-flexible-and-able-to-mock-methods.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/36000
|
||||||
|
Patch17: 0017-Check-for-single-quote-before-splitting-on-single-qu.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/36139
|
||||||
|
# https://github.com/saltstack/salt/pull/36158
|
||||||
|
# https://github.com/saltstack/salt/pull/36227
|
||||||
|
Patch18: 0018-Unit-tests-fixes-for-2016.3.2.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/35715
|
||||||
|
# https://github.com/saltstack/salt/pull/35983
|
||||||
|
# https://github.com/saltstack/salt/pull/34826
|
||||||
|
Patch19: 0019-Fix-snapper_test-for-python26.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/36263
|
||||||
|
Patch20: 0020-Integration-tests-fixes-for-2016.3.2.patch
|
||||||
|
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/36495
|
||||||
|
Patch21: 0021-Fix-pkg.upgrade-for-zypper.patch
|
||||||
|
|
||||||
|
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||||
BuildRequires: logrotate
|
BuildRequires: logrotate
|
||||||
@ -120,17 +149,27 @@ BuildRequires: python-salt-testing >= 2015.2.16
|
|||||||
BuildRequires: python-unittest2
|
BuildRequires: python-unittest2
|
||||||
BuildRequires: python-xml
|
BuildRequires: python-xml
|
||||||
%endif
|
%endif
|
||||||
%if %{with docs}
|
%if %{with builddocs}
|
||||||
#for docs
|
|
||||||
BuildRequires: python-sphinx
|
BuildRequires: python-sphinx
|
||||||
%endif
|
%endif
|
||||||
|
%if 0%{?suse_version} > 1010
|
||||||
|
BuildRequires: fdupes
|
||||||
|
%endif
|
||||||
|
|
||||||
Requires(pre): %{_sbindir}/groupadd
|
Requires(pre): %{_sbindir}/groupadd
|
||||||
Requires(pre): %{_sbindir}/useradd
|
Requires(pre): %{_sbindir}/useradd
|
||||||
|
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
Requires(pre): %fillup_prereq
|
Requires(pre): %fillup_prereq
|
||||||
Requires(pre): pwdutils
|
Requires(pre): pwdutils
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
Requires(pre): dbus-1
|
||||||
|
%else
|
||||||
|
Requires(pre): dbus
|
||||||
|
%endif
|
||||||
|
|
||||||
Requires: logrotate
|
Requires: logrotate
|
||||||
Requires: python
|
Requires: python
|
||||||
#
|
#
|
||||||
@ -432,7 +471,11 @@ cp %{S:1} .
|
|||||||
%patch4 -p1
|
%patch4 -p1
|
||||||
%patch5 -p1
|
%patch5 -p1
|
||||||
%patch6 -p1
|
%patch6 -p1
|
||||||
|
|
||||||
|
# This is SUSE-only patch
|
||||||
|
%if 0%{?suse_version}
|
||||||
%patch7 -p1
|
%patch7 -p1
|
||||||
|
%endif
|
||||||
%patch8 -p1
|
%patch8 -p1
|
||||||
%patch9 -p1
|
%patch9 -p1
|
||||||
%patch10 -p1
|
%patch10 -p1
|
||||||
@ -442,16 +485,24 @@ cp %{S:1} .
|
|||||||
%patch14 -p1
|
%patch14 -p1
|
||||||
%patch15 -p1
|
%patch15 -p1
|
||||||
%patch16 -p1
|
%patch16 -p1
|
||||||
|
|
||||||
# This is SUSE-only patch
|
|
||||||
%if 0%{?suse_version}
|
|
||||||
%patch17 -p1
|
%patch17 -p1
|
||||||
%endif
|
%patch18 -p1
|
||||||
|
%patch19 -p1
|
||||||
|
%patch20 -p1
|
||||||
|
%patch21 -p1
|
||||||
|
|
||||||
%build
|
%build
|
||||||
python setup.py --salt-transport=both build
|
python setup.py --salt-transport=both build
|
||||||
|
|
||||||
%if %{with docs}
|
%if %{with docs} && %{without builddocs}
|
||||||
|
# extract docs from the tarball
|
||||||
|
mkdir -p doc/_build
|
||||||
|
pushd doc/_build/
|
||||||
|
tar xfv %{S:3}
|
||||||
|
popd
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if %{with docs} && %{with builddocs}
|
||||||
## documentation
|
## documentation
|
||||||
cd doc && make html && rm _build/html/.buildinfo && rm _build/html/_images/proxy_minions.png && cd _build/html && chmod -R -x+X *
|
cd doc && make html && rm _build/html/.buildinfo && rm _build/html/_images/proxy_minions.png && cd _build/html && chmod -R -x+X *
|
||||||
%endif
|
%endif
|
||||||
@ -498,7 +549,7 @@ install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/minion
|
|||||||
## Install Zypper plugins only on SUSE machines
|
## Install Zypper plugins only on SUSE machines
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
install -Dd -m 0750 %{buildroot}%{_prefix}/lib/zypp/plugins/commit
|
install -Dd -m 0750 %{buildroot}%{_prefix}/lib/zypp/plugins/commit
|
||||||
%{__install} scripts/zypper/plugins/commit/susemanager %{buildroot}%{_prefix}/lib/zypp/plugins/commit/susemanager
|
%{__install} scripts/zypper/plugins/commit/zyppnotify %{buildroot}%{_prefix}/lib/zypp/plugins/commit/zyppnotify
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
## install init and systemd scripts
|
## install init and systemd scripts
|
||||||
@ -507,6 +558,7 @@ install -Dpm 0644 pkg/salt-master.service %{buildroot}%{_unitdir}/salt-master.se
|
|||||||
install -Dpm 0644 pkg/salt-minion.service %{buildroot}%{_unitdir}/salt-minion.service
|
install -Dpm 0644 pkg/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-syndic.service %{buildroot}%{_unitdir}/salt-syndic.service
|
||||||
install -Dpm 0644 pkg/salt-api.service %{buildroot}%{_unitdir}/salt-api.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
|
||||||
ln -s service %{buildroot}%{_sbindir}/rcsalt-master
|
ln -s service %{buildroot}%{_sbindir}/rcsalt-master
|
||||||
ln -s service %{buildroot}%{_sbindir}/rcsalt-syndic
|
ln -s service %{buildroot}%{_sbindir}/rcsalt-syndic
|
||||||
ln -s service %{buildroot}%{_sbindir}/rcsalt-minion
|
ln -s service %{buildroot}%{_sbindir}/rcsalt-minion
|
||||||
@ -558,6 +610,11 @@ install -Dpm 0644 pkg/fish-completions/* %{buildroot}%{fish_completions_dir}
|
|||||||
echo "transport: raet" > %{buildroot}%{_sysconfdir}/salt/master.d/transport-raet.conf
|
echo "transport: raet" > %{buildroot}%{_sysconfdir}/salt/master.d/transport-raet.conf
|
||||||
echo "transport: raet" > %{buildroot}%{_sysconfdir}/salt/minion.d/transport-raet.conf
|
echo "transport: raet" > %{buildroot}%{_sysconfdir}/salt/minion.d/transport-raet.conf
|
||||||
|
|
||||||
|
%if 0%{?suse_version} > 1020
|
||||||
|
%fdupes %{buildroot}%{_docdir}
|
||||||
|
%fdupes %{buildroot}%{python_sitelib}
|
||||||
|
%endif
|
||||||
|
|
||||||
%check
|
%check
|
||||||
%if %{with test}
|
%if %{with test}
|
||||||
python setup.py test --runtests-opts=-u
|
python setup.py test --runtests-opts=-u
|
||||||
@ -567,9 +624,50 @@ python setup.py test --runtests-opts=-u
|
|||||||
getent group salt >/dev/null || %{_sbindir}/groupadd -r 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 /srv/salt -s /bin/false -c "salt-master daemon" salt
|
||||||
|
|
||||||
%if %{with systemd}
|
|
||||||
%post
|
%post
|
||||||
|
%if %{with systemd}
|
||||||
systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
||||||
|
%else
|
||||||
|
dbus-uuidgen --ensure
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%preun proxy
|
||||||
|
%if %{with systemd}
|
||||||
|
%service_del_preun salt-proxy@.service
|
||||||
|
%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
|
||||||
|
|
||||||
|
%pre proxy
|
||||||
|
%if %{with systemd}
|
||||||
|
%service_add_pre salt-proxy@.service
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%post proxy
|
||||||
|
%if %{with systemd}
|
||||||
|
%service_add_post salt-proxy@.service
|
||||||
|
%fillup_only
|
||||||
|
%else
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
%fillup_and_insserv
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%postun proxy
|
||||||
|
%if %{with systemd}
|
||||||
|
%service_del_postun salt-proxy@.service
|
||||||
|
%else
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
%insserv_cleanup
|
||||||
|
%restart_on_update salt-proxy
|
||||||
|
%endif
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%preun syndic
|
%preun syndic
|
||||||
@ -631,6 +729,20 @@ systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%post master
|
%post master
|
||||||
|
if [ $1 -eq 2 ] ; then
|
||||||
|
# Upgrading from an earlier version. If this is from 2014, where daemons
|
||||||
|
# ran as root, we need to chown some stuff to salt in order for the new
|
||||||
|
# version to actually work. It seems a manual restart of salt-master may
|
||||||
|
# still be required, but at least this will actually work given the file
|
||||||
|
# ownership is correct.
|
||||||
|
for file in master.{pem,pub} ; do
|
||||||
|
[ -f /etc/salt/pki/master/$file ] && chown salt /etc/salt/pki/master/$file
|
||||||
|
done
|
||||||
|
for dir in file_lists minions jobs ; do
|
||||||
|
[ -d /var/cache/salt/master/$dir ] && chown -R salt:salt /var/cache/salt/master/$dir
|
||||||
|
done
|
||||||
|
true
|
||||||
|
fi
|
||||||
%if %{with systemd}
|
%if %{with systemd}
|
||||||
%service_add_post salt-master.service
|
%service_add_post salt-master.service
|
||||||
%fillup_only
|
%fillup_only
|
||||||
@ -779,11 +891,12 @@ systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
|||||||
%dir %attr(0750, root, root) %{_sysconfdir}/salt/minion.d/
|
%dir %attr(0750, root, root) %{_sysconfdir}/salt/minion.d/
|
||||||
%dir %attr(0750, root, root) %{_sysconfdir}/salt/pki/minion/
|
%dir %attr(0750, root, root) %{_sysconfdir}/salt/pki/minion/
|
||||||
%dir %attr(0750, root, root) %{_localstatedir}/cache/salt/minion/
|
%dir %attr(0750, root, root) %{_localstatedir}/cache/salt/minion/
|
||||||
|
#%dir %ghost %attr(0750, root, salt) %{_localstatedir}/run/salt/minion
|
||||||
%{_sbindir}/rcsalt-minion
|
%{_sbindir}/rcsalt-minion
|
||||||
|
|
||||||
# Install plugin only on SUSE machines
|
# Install plugin only on SUSE machines
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
%{_prefix}/lib/zypp/plugins/commit/susemanager
|
%{_prefix}/lib/zypp/plugins/commit/zyppnotify
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if %{with systemd}
|
%if %{with systemd}
|
||||||
@ -796,6 +909,9 @@ systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
|||||||
%defattr(-,root,root)
|
%defattr(-,root,root)
|
||||||
%{_bindir}/salt-proxy
|
%{_bindir}/salt-proxy
|
||||||
%{_mandir}/man1/salt-proxy.1.gz
|
%{_mandir}/man1/salt-proxy.1.gz
|
||||||
|
%if %{with systemd}
|
||||||
|
%{_unitdir}/salt-proxy@.service
|
||||||
|
%endif
|
||||||
|
|
||||||
%files master
|
%files master
|
||||||
%defattr(-,root,root)
|
%defattr(-,root,root)
|
||||||
@ -835,6 +951,7 @@ systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
|||||||
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/roots/
|
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/roots/
|
||||||
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/syndics/
|
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/syndics/
|
||||||
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/tokens/
|
%dir %attr(0750, salt, salt) %{_localstatedir}/cache/salt/master/tokens/
|
||||||
|
#%dir %ghost %attr(0750, salt, salt) %{_localstatedir}/run/salt/master/
|
||||||
|
|
||||||
%files raet
|
%files raet
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
@ -848,17 +965,19 @@ systemd-tmpfiles --create /usr/lib/tmpfiles.d/salt.conf || true
|
|||||||
%{_bindir}/salt-unity
|
%{_bindir}/salt-unity
|
||||||
%{_mandir}/man1/salt-unity.1.gz
|
%{_mandir}/man1/salt-unity.1.gz
|
||||||
%{_mandir}/man1/salt-call.1.gz
|
%{_mandir}/man1/salt-call.1.gz
|
||||||
|
%{_mandir}/man1/spm.1.gz
|
||||||
%config(noreplace) %{_sysconfdir}/logrotate.d/salt
|
%config(noreplace) %{_sysconfdir}/logrotate.d/salt
|
||||||
%{python_sitelib}/*
|
%{python_sitelib}/*
|
||||||
%exclude %{python_sitelib}/salt/cloud/deploy/*.sh
|
%exclude %{python_sitelib}/salt/cloud/deploy/*.sh
|
||||||
%attr(755,root,root)%{python_sitelib}/salt/cloud/deploy/*.sh
|
%attr(755,root,root)%{python_sitelib}/salt/cloud/deploy/*.sh
|
||||||
%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE
|
%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE
|
||||||
#
|
#
|
||||||
%dir %attr(0750, root, salt) %{_sysconfdir}/salt
|
%dir %attr(0750, root, salt) %{_sysconfdir}/salt
|
||||||
%dir %attr(0750, root, salt) %{_sysconfdir}/salt/pki
|
%dir %attr(0750, root, salt) %{_sysconfdir}/salt/pki
|
||||||
%dir %attr(0750, salt, salt) %{_localstatedir}/log/salt
|
%dir %attr(0750, salt, salt) %{_localstatedir}/log/salt
|
||||||
%dir %attr(0750, root, salt) %{_localstatedir}/cache/salt
|
%dir %attr(0750, root, salt) %{_localstatedir}/cache/salt
|
||||||
%dir %attr(0750, root, salt) /srv/spm
|
#%dir %ghost %attr(0750, root, salt) %{_localstatedir}/run/salt
|
||||||
|
%dir %attr(0750, root, salt) /srv/spm
|
||||||
%if %{with systemd}
|
%if %{with systemd}
|
||||||
/usr/lib/tmpfiles.d/salt.conf
|
/usr/lib/tmpfiles.d/salt.conf
|
||||||
%endif
|
%endif
|
||||||
|
64
update-documentation.sh
Normal file
64
update-documentation.sh
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Update html.tar.bz2 documentation tarball
|
||||||
|
# Author: Bo Maryniuk <bo@suse.de>
|
||||||
|
#
|
||||||
|
|
||||||
|
function check_env() {
|
||||||
|
for cmd in "sphinx-build" "make" "quilt"; do
|
||||||
|
if [ -z "$(which $cmd 2>/dev/null)" ]; then
|
||||||
|
echo "Error: '$cmd' is missing."
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function quilt_setup() {
|
||||||
|
quilt setup salt.spec
|
||||||
|
cd $1
|
||||||
|
quilt push -a
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_docs() {
|
||||||
|
cd $1
|
||||||
|
make html
|
||||||
|
rm _build/html/.buildinfo
|
||||||
|
cd _build/html
|
||||||
|
chmod -R -x+X *
|
||||||
|
cd ..
|
||||||
|
tar cvf - html | bzip2 > /tmp/html.tar.bz2
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_changelog() {
|
||||||
|
mv salt.changes salt.changes.previous
|
||||||
|
TIME=$(date -u +'%a %b %d %T %Z %Y')
|
||||||
|
MAIL=$1
|
||||||
|
SEP="-------------------------------------------------------------------"
|
||||||
|
cat <<EOF > salt.changes
|
||||||
|
$SEP
|
||||||
|
$TIME - $MAIL
|
||||||
|
|
||||||
|
- Updated html.tar.bz2 documentation tarball.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
cat salt.changes.previous >> salt.changes
|
||||||
|
rm salt.changes.previous
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 <your e-mail>"
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_env;
|
||||||
|
START=$(pwd)
|
||||||
|
SRC_DIR="salt-$(cat salt.spec | grep ^Version: | cut -d: -f2 | sed -e 's/[[:blank:]]//g')";
|
||||||
|
quilt_setup $SRC_DIR
|
||||||
|
build_docs doc
|
||||||
|
|
||||||
|
cd $START
|
||||||
|
rm -rf $SRC_DIR
|
||||||
|
mv /tmp/html.tar.bz2 $START
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
|
echo "---------------"
|
Loading…
x
Reference in New Issue
Block a user