diff --git a/Gemfile b/Gemfile
index b014c8fa1b..0326139a50 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,6 +2,7 @@
source 'https://rubygems.org'
+# Point this back at the test-kitchen package after 1.23.3 is relased
gem 'test-kitchen', '~>1.23.3'
gem 'kitchen-salt', '~>0.4.1'
gem 'kitchen-sync'
diff --git a/pkg/osx/build_pkg.sh b/pkg/osx/build_pkg.sh
index d31e4d1723..f1dbd188c7 100755
--- a/pkg/osx/build_pkg.sh
+++ b/pkg/osx/build_pkg.sh
@@ -158,13 +158,20 @@ echo -n -e "\033]0;Build_Pkg: Add Version to .xml\007"
if [ "$PYVER" == "2" ]; then
TITLE="Salt $VERSION"
DESC="Salt $VERSION with Python 2"
+ SEDSTR="s/@PY2@/_py2/g"
else
TITLE="Salt $VERSION (Python 3)"
DESC="Salt $VERSION with Python 3"
+ SEDSTR="s/@PY2@//g"
fi
cd $PKGRESOURCES
cp distribution.xml.dist distribution.xml
+
+# Select the appropriate welcome text
+# This is only necessary until Sodium, then this can be removed
+sed -E -i '' "$SEDSTR" distribution.xml
+
SEDSTR="s/@TITLE@/$TITLE/g"
sed -E -i '' "$SEDSTR" distribution.xml
diff --git a/pkg/osx/distribution.xml.dist b/pkg/osx/distribution.xml.dist
index d31063f5f4..ff55164d05 100644
--- a/pkg/osx/distribution.xml.dist
+++ b/pkg/osx/distribution.xml.dist
@@ -12,11 +12,12 @@
hostArchitectures="@CPUARCH@" />
-
+ scaling="proportional"
+ alignment="bottomleft" />
-
diff --git a/pkg/osx/pkg-resources/logo.png b/pkg/osx/pkg-resources/logo.png
new file mode 100644
index 0000000000..7f05ffe2b1
Binary files /dev/null and b/pkg/osx/pkg-resources/logo.png differ
diff --git a/pkg/osx/pkg-resources/saltstack.png b/pkg/osx/pkg-resources/saltstack.png
deleted file mode 100644
index abdc3a3707..0000000000
Binary files a/pkg/osx/pkg-resources/saltstack.png and /dev/null differ
diff --git a/pkg/osx/pkg-resources/welcome_py2.rtf b/pkg/osx/pkg-resources/welcome_py2.rtf
new file mode 100644
index 0000000000..345b923d86
--- /dev/null
+++ b/pkg/osx/pkg-resources/welcome_py2.rtf
@@ -0,0 +1,32 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf340
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\froman\fcharset0 Times-Roman;\f2\fnil\fcharset0 PTMono-Regular;
+}
+{\colortbl;\red255\green255\blue255;\red0\green0\blue233;}
+\vieww28300\viewh14680\viewkind0
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
+
+\f0\fs24 \cf0 WARNING: Python 2 Support will be discontinued in Sodium. Salt will only ship Python 3 installers starting with the Sodium release.\
+\
+\pard\pardeftab720\sl280\sa240\partightenfactor0
+{\field{\*\fldinst{HYPERLINK "http://saltstack.com/"}}{\fldrslt
+\f1 \cf2 \expnd0\expndtw0\kerning0
+\ul \ulc2 SaltStack}}
+\f1 \expnd0\expndtw0\kerning0
+ is extremely fast and scalable systems and configuration management software for predictive orchestration, cloud and data center automation, server provisioning, application deployment and more. \
+Documentation for Salt is available at {\field{\*\fldinst{HYPERLINK "http://docs.saltstack.com/"}}{\fldrslt \cf2 \ul \ulc2 http://docs.saltstack.com}} \
+This package will install Salt on your Mac. Salt installs into
+\f2\fs22 /opt/salt
+\f1\fs24 .\
+Sample configuration files (
+\f2\fs22 master.dist
+\f1\fs24 and
+\f2\fs22 minion.dist
+\f1\fs24 ) will be installed to
+\f2\fs22 /etc/salt
+\f1\fs24 . Create copies of them without the '
+\f2\fs22 .dist
+\f1\fs24 ' in the filename and edit as you see fit.\
+This Salt package uses a custom-built Python. To install additional Python modules for Salt, use the associated 'pip' binary. For example, if you need LDAP support in Salt you will need the 'python-ldap' module. Install it with
+\f2\fs22 /opt/salt/bin/pip install python-ldap
+\f1\fs24 \
+Note that some Python modules need a compiler available. Installing Apple's Xcode Command Line Tools should provide the necessary utilities. Alternatively, {\field{\*\fldinst{HYPERLINK "http://macports.org/"}}{\fldrslt \cf2 \ul \ulc2 MacPorts}}, {\field{\*\fldinst{HYPERLINK "http://finkproject.org/"}}{\fldrslt \cf2 \ul \ulc2 Fink}}, or {\field{\*\fldinst{HYPERLINK "http://brew.sh/"}}{\fldrslt \cf2 \ul \ulc2 Homebrew}} may provide what you need.}
\ No newline at end of file
diff --git a/pkg/windows/installer/Salt-Minion-Setup.nsi b/pkg/windows/installer/Salt-Minion-Setup.nsi
index d4560ed719..dfc3be0cdb 100644
--- a/pkg/windows/installer/Salt-Minion-Setup.nsi
+++ b/pkg/windows/installer/Salt-Minion-Setup.nsi
@@ -70,6 +70,20 @@ ${StrStrAdv}
!define MUI_ICON "salt.ico"
!define MUI_UNICON "salt.ico"
!define MUI_WELCOMEFINISHPAGE_BITMAP "panel.bmp"
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP "panel.bmp"
+
+!if "${PYTHON_VERSION}" == "2"
+ !define MUI_WELCOMEPAGE_TEXT "\
+ WARNING: Python 2 Support will be discontinued in Sodium. Salt will only ship Python 3 \
+ installers starting with the Sodium release.$\r$\n\
+ $\r$\n\
+ Setup will guide you through the installation of ${PRODUCT_NAME} ${PRODUCT_VERSION}.$\r$\n\
+ $\r$\n\
+ It is recommended that you close all other applications before starting Setup. This will make it possible to \
+ update relevant system files without having to reboot your computer.$\r$\n\
+ $\r$\n\
+ Click Next to continue."
+!endif
# Welcome page
!insertmacro MUI_PAGE_WELCOME
diff --git a/pkg/windows/installer/panel.bmp b/pkg/windows/installer/panel.bmp
index 5a2720bca2..fce9aaac3e 100644
Binary files a/pkg/windows/installer/panel.bmp and b/pkg/windows/installer/panel.bmp differ
diff --git a/pkg/windows/installer/salt.ico b/pkg/windows/installer/salt.ico
index 47380de47c..4ac60ca2e2 100644
Binary files a/pkg/windows/installer/salt.ico and b/pkg/windows/installer/salt.ico differ
diff --git a/salt/_compat.py b/salt/_compat.py
index 3d208bae0b..c10b82c0c2 100644
--- a/salt/_compat.py
+++ b/salt/_compat.py
@@ -144,6 +144,12 @@ class IPv6AddressScoped(ipaddress.IPv6Address):
:param address:
'''
+ # pylint: disable-all
+ if not hasattr(self, '_is_packed_binary'):
+ # This method (below) won't be around for some Python 3 versions
+ # and we need check this differently anyway
+ self._is_packed_binary = lambda p: isinstance(p, bytes)
+ # pylint: enable-all
if isinstance(address, string_types) and '%' in address:
buff = address.split('%')
if len(buff) != 2:
diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py
index 22505b9cf2..3c469dc833 100644
--- a/salt/modules/win_lgpo.py
+++ b/salt/modules/win_lgpo.py
@@ -5083,8 +5083,8 @@ def _findOptionValueAdvAudit(option):
field_names = _get_audit_defaults('fieldnames')
# If the file doesn't exist anywhere, create it with default
# fieldnames
- __salt__['file.touch'](f_audit)
- __salt__['file.append'](f_audit, ','.join(field_names))
+ __salt__['file.mkdir'](os.path.dirname(f_audit))
+ __salt__['file.write'](f_audit, ','.join(field_names))
audit_settings = {}
with salt.utils.files.fopen(f_audit, mode='r') as csv_file:
@@ -5187,6 +5187,7 @@ def _set_audit_file_data(option, value):
# Copy the temporary csv file over the existing audit.csv in both
# locations if a value was written
__salt__['file.copy'](f_temp.name, f_audit, remove_existing=True)
+ __salt__['file.mkdir'](os.path.dirname(f_audit_gpo))
__salt__['file.copy'](f_temp.name, f_audit_gpo, remove_existing=True)
finally:
f_temp.close()
diff --git a/salt/modules/win_timezone.py b/salt/modules/win_timezone.py
index 8fe0ab9ff0..135d908f11 100644
--- a/salt/modules/win_timezone.py
+++ b/salt/modules/win_timezone.py
@@ -205,17 +205,22 @@ def get_zone():
Returns:
str: Timezone in unix format
+ Raises:
+ CommandExecutionError: If timezone could not be gathered
+
CLI Example:
.. code-block:: bash
salt '*' timezone.get_zone
'''
- win_zone = __utils__['reg.read_value'](
- hive='HKLM',
- key='SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation',
- vname='TimeZoneKeyName')['vdata']
- return mapper.get_unix(win_zone.lower(), 'Unknown')
+ cmd = ['tzutil', '/g']
+ res = __salt__['cmd.run_all'](cmd, python_shell=False)
+ if res['retcode'] or not res['stdout']:
+ raise CommandExecutionError('tzutil encountered an error getting '
+ 'timezone',
+ info=res)
+ return mapper.get_unix(res['stdout'].lower(), 'Unknown')
def get_offset():
diff --git a/salt/scripts.py b/salt/scripts.py
index 1bbc83a300..71120366e0 100644
--- a/salt/scripts.py
+++ b/salt/scripts.py
@@ -20,6 +20,7 @@ from random import randint
# Import salt libs
from salt.exceptions import SaltSystemExit, SaltClientError, SaltReqTimeoutError
import salt.defaults.exitcodes # pylint: disable=unused-import
+import salt.ext.six as six
log = logging.getLogger(__name__)
@@ -93,6 +94,16 @@ def salt_master():
Start the salt master.
'''
import salt.cli.daemons
+# REMOVEME after Python 2.7 support is dropped (also the six import)
+ if six.PY2:
+ from salt.utils.versions import warn_until
+ # Message borrowed from pip's deprecation warning
+ warn_until('Sodium',
+ 'Python 2.7 will reach the end of its life on January 1st,'
+ ' 2020. Please upgrade your Python as Python 2.7 won\'t be'
+ ' maintained after that date. Salt will drop support for'
+ ' Python 2.7 in the Sodium release or later.')
+# END REMOVEME
master = salt.cli.daemons.Master()
master.start()
@@ -179,6 +190,16 @@ def salt_minion():
minion = salt.cli.daemons.Minion()
minion.start()
return
+# REMOVEME after Python 2.7 support is dropped (also the six import)
+ elif six.PY2:
+ from salt.utils.versions import warn_until
+ # Message borrowed from pip's deprecation warning
+ warn_until('Sodium',
+ 'Python 2.7 will reach the end of its life on January 1st,'
+ ' 2020. Please upgrade your Python as Python 2.7 won\'t be'
+ ' maintained after that date. Salt will drop support for'
+ ' Python 2.7 in the Sodium release or later.')
+# END REMOVEME
if '--disable-keepalive' in sys.argv:
sys.argv.remove('--disable-keepalive')
diff --git a/salt/transport/ipc.py b/salt/transport/ipc.py
index 8081349acf..40a172991d 100644
--- a/salt/transport/ipc.py
+++ b/salt/transport/ipc.py
@@ -219,7 +219,12 @@ class IPCServer(object):
self.sock.close()
def __del__(self):
- self.close()
+ try:
+ self.close()
+ except TypeError:
+ # This is raised when Python's GC has collected objects which
+ # would be needed when calling self.close()
+ pass
class IPCClient(object):
@@ -360,16 +365,21 @@ class IPCClient(object):
yield tornado.gen.sleep(1)
def __del__(self):
- with self._refcount_lock:
- # Make sure we actually close no matter if something
- # went wrong with our ref counting
- self._refcount = 1
try:
- self.close()
- except socket.error as exc:
- if exc.errno != errno.EBADF:
- # If its not a bad file descriptor error, raise
- raise
+ with self._refcount_lock:
+ # Make sure we actually close no matter if something
+ # went wrong with our ref counting
+ self._refcount = 1
+ try:
+ self.close()
+ except socket.error as exc:
+ if exc.errno != errno.EBADF:
+ # If its not a bad file descriptor error, raise
+ raise
+ except TypeError:
+ # This is raised when Python's GC has collected objects which
+ # would be needed when calling self.close()
+ pass
def close(self):
'''
@@ -610,7 +620,12 @@ class IPCMessagePublisher(object):
self.sock.close()
def __del__(self):
- self.close()
+ try:
+ self.close()
+ except TypeError:
+ # This is raised when Python's GC has collected objects which
+ # would be needed when calling self.close()
+ pass
class IPCMessageSubscriber(IPCClient):
diff --git a/tests/integration/modules/test_win_dns_client.py b/tests/integration/modules/test_win_dns_client.py
index 33997d6ab4..1893b5ec5d 100644
--- a/tests/integration/modules/test_win_dns_client.py
+++ b/tests/integration/modules/test_win_dns_client.py
@@ -22,8 +22,12 @@ class WinDNSTest(ModuleCase):
'''
Test add and removing a dns server
'''
+ # Get a list of interfaces on the system
+ interfaces = self.run_function('network.interfaces_names')
+ skipIf(interfaces.count == 0, 'This test requires a network interface')
+
+ interface = interfaces[0]
dns = '8.8.8.8'
- interface = 'Ethernet'
# add dns server
self.assertTrue(self.run_function('win_dns_client.add_dns', [dns, interface], index=42))
diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py
index 3c361377cf..456e06826b 100644
--- a/tests/integration/states/test_file.py
+++ b/tests/integration/states/test_file.py
@@ -148,7 +148,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
remove files created in previous tests
'''
user = 'salt'
- if user in str(self.run_function('user.list_users', [user])):
+ if user in str(self.run_function('user.list_users')):
self.run_function('user.delete', [user])
for path in (FILEPILLAR, FILEPILLARDEF, FILEPILLARGIT):
diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py
index 917aadc8e1..445df0c98a 100644
--- a/tests/unit/grains/test_core.py
+++ b/tests/unit/grains/test_core.py
@@ -601,7 +601,7 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
self.assertIn(returned_grains['windowsdomaintype'], valid_types)
valid_releases = ['Vista', '7', '8', '8.1', '10', '2008Server',
'2008ServerR2', '2012Server', '2012ServerR2',
- '2016Server']
+ '2016Server', '2019Server']
self.assertIn(returned_grains['osrelease'], valid_releases)
@skipIf(not salt.utils.platform.is_linux(), 'System is not Linux')
diff --git a/tests/unit/modules/test_win_timezone.py b/tests/unit/modules/test_win_timezone.py
index 68c20bd306..6916f08de5 100644
--- a/tests/unit/modules/test_win_timezone.py
+++ b/tests/unit/modules/test_win_timezone.py
@@ -3,18 +3,15 @@
:codeauthor: Jayesh Kariya
'''
# Import Python Libs
-from __future__ import absolute_import, unicode_literals, print_function
-
-# Import Salt Testing Libs
-from tests.support.mixins import LoaderModuleMockMixin
-from tests.support.unit import TestCase, skipIf
-from tests.support.mock import (
- MagicMock,
- patch
-)
+from __future__ import absolute_import, print_function, unicode_literals
# Import Salt Libs
import salt.modules.win_timezone as win_timezone
+from salt.exceptions import CommandExecutionError
+# Import Salt Testing Libs
+from tests.support.mixins import LoaderModuleMockMixin
+from tests.support.mock import MagicMock, patch
+from tests.support.unit import TestCase, skipIf
@skipIf(not win_timezone.HAS_PYTZ, 'This test requires pytz')
@@ -25,20 +22,36 @@ class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin):
def setup_loader_modules(self):
return {win_timezone: {}}
- # 'get_zone' function tests: 1
+ # 'get_zone' function tests: 3
def test_get_zone(self):
'''
Test if it get current timezone (i.e. Asia/Calcutta)
'''
- mock_read = MagicMock(side_effect=[{'vdata': 'India Standard Time'},
- {'vdata': 'Indian Standard Time'}])
+ mock_read_ok = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'India Standard Time'})
- with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}):
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read_ok}):
self.assertEqual(win_timezone.get_zone(), 'Asia/Calcutta')
+ mock_read_error = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'Indian Standard Time'})
+
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read_error}):
self.assertEqual(win_timezone.get_zone(), 'Unknown')
+ mock_read_fatal = MagicMock(return_value={'pid': 78,
+ 'retcode': 1,
+ 'stderr': '',
+ 'stdout': ''})
+
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read_fatal}):
+ self.assertRaises(CommandExecutionError, win_timezone.get_zone)
+
# 'get_offset' function tests: 1
def test_get_offset(self):
@@ -49,10 +62,12 @@ class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin):
# New Delhi\nIndia Standard Time')
# mock_cmd = MagicMock(side_effect=['India Standard Time', time])
# with patch.dict(win_timezone.__salt__, {'cmd.run': mock_cmd}):
+ mock_read = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'India Standard Time'})
- mock_read = MagicMock(return_value={'vdata': 'India Standard Time'})
-
- with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}):
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read}):
self.assertEqual(win_timezone.get_offset(), '+0530')
# 'get_zonecode' function tests: 1
@@ -61,9 +76,12 @@ class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin):
'''
Test if it get current timezone (i.e. PST, MDT, etc)
'''
- mock_read = MagicMock(return_value={'vdata': 'India Standard Time'})
+ mock_read = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'India Standard Time'})
- with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}):
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read}):
self.assertEqual(win_timezone.get_zonecode(), 'IST')
# 'set_zone' function tests: 1
@@ -72,14 +90,17 @@ class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin):
'''
Test if it unlinks, then symlinks /etc/localtime to the set timezone.
'''
- mock_cmd = MagicMock(return_value={'pid': 78,
- 'retcode': 0,
- 'stderr': '',
- 'stdout': ''})
- mock_read = MagicMock(return_value={'vdata': 'India Standard Time'})
+ mock_write = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': ''})
+ mock_read = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'India Standard Time'})
- with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_cmd}), \
- patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}):
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_write}), \
+ patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read}):
self.assertTrue(win_timezone.set_zone('Asia/Calcutta'))
@@ -91,9 +112,12 @@ class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin):
the one set in /etc/localtime. Returns True if they match,
and False if not. Mostly useful for running state checks.
'''
- mock_read = MagicMock(return_value={'vdata': 'India Standard Time'})
+ mock_read = MagicMock(return_value={'pid': 78,
+ 'retcode': 0,
+ 'stderr': '',
+ 'stdout': 'India Standard Time'})
- with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}):
+ with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_read}):
self.assertTrue(win_timezone.zone_compare('Asia/Calcutta'))
# 'get_hwclock' function tests: 1