Merge pull request #30775 from Unity-Technologies/feature/mac_defaults

Added the ability to set OS X settings using defaults
This commit is contained in:
Mike Place 2016-02-02 07:47:49 -07:00
commit 330abcad12
4 changed files with 390 additions and 0 deletions

View File

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
'''
Set defaults on Mac OS
'''
# Import python libs
from __future__ import absolute_import
import logging
# Import salt libs
import salt.utils
log = logging.getLogger(__name__)
__virtualname__ = 'macdefaults'
def __virtual__():
'''
Only work on Mac OS
'''
if salt.utils.is_darwin():
return __virtualname__
return False
def write(domain, key, value, type='string', user=None):
'''
Write a default to the system
CLI Example:
.. code-block:: bash
salt '*' macdefaults.write com.apple.CrashReporter DialogType Server
salt '*' macdefaults.write NSGlobalDomain ApplePersistence True type=bool
domain
The name of the domain to write to
key
The key of the given domain to write to
value
The value to write to the given key
type
The type of value to be written, vaid types are string, data, int[eger],
float, bool[ean], date, array, array-add, dict, dict-add
user
The user to write the defaults to
'''
if type == 'bool' or type == 'boolean':
if value is True:
value = 'TRUE'
elif value is False:
value = 'FALSE'
cmd = 'defaults write "{0}" "{1}" -{2} "{3}"'.format(domain, key, type, value)
return __salt__['cmd.run_all'](cmd, runas=user)
def read(domain, key, user=None):
'''
Write a default to the system
CLI Example:
.. code-block:: bash
salt '*' macdefaults.read com.apple.CrashReporter DialogType
salt '*' macdefaults.read NSGlobalDomain ApplePersistence
domain
The name of the domain to read from
key
The key of the given domain to read from
user
The user to write the defaults to
'''
cmd = 'defaults read "{0}" "{1}"'.format(domain, key)
return __salt__['cmd.run'](cmd, runas=user)

View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
'''
Writing/reading defaults from an OS X minion
=======================
'''
# Import python libs
from __future__ import absolute_import
import logging
# Import salt libs
import salt.utils
log = logging.getLogger(__name__)
__virtualname__ = 'macdefaults'
def __virtual__():
'''
Only work on Mac OS
'''
if salt.utils.is_darwin():
return __virtualname__
return False
def write(name, domain, value, vtype='string', user=None):
'''
Write a default to the system
name
The key of the given domain to write to
domain
The name of the domain to write to
value
The value to write to the given key
vtype
The type of value to be written, vaid types are string, data, int[eger],
float, bool[ean], date, array, array-add, dict, dict-add
user
The user to write the defaults to
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
def safe_cast(val, to_type, default=None):
try:
return to_type(val)
except ValueError:
return default
current_value = __salt__['macdefaults.read'](domain, name, user)
if (vtype in ['bool', 'boolean']) and ((value in [True, 'TRUE', 'YES'] and current_value == '1') or
(value in [False, 'FALSE', 'NO'] and current_value == '0')):
ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value)
elif vtype in ['int', 'integer'] and safe_cast(current_value, int) == safe_cast(value, int):
ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value)
elif current_value == value:
ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value)
else:
out = __salt__['macdefaults.write'](domain, name, value, vtype, user)
if out['retcode'] != 0:
ret['result'] = False
ret['comment'] = 'Failed to write default. {0}'.format(out['stdout'])
else:
ret['changes']['written'] = '{0} {1} is set to {2}'.format(domain, name, value)
return ret

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Import Python libs
from __future__ import absolute_import
# Import Salt Libs
from salt.modules import mac_defaults as macdefaults
# Import Salt Testing Libs
from salttesting import TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
MagicMock,
patch
)
ensure_in_syspath('../../')
macdefaults.__salt__ = {}
class MacDefaultsTestCase(TestCase):
def test_write_default(self):
'''
Test writing a default setting
'''
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}):
macdefaults.write('com.apple.CrashReporter', 'DialogType', 'Server')
mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "DialogType" -string "Server"',
runas=None)
def test_write_with_user(self):
'''
Test writing a default setting with a specific user
'''
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}):
macdefaults.write('com.apple.CrashReporter', 'DialogType', 'Server', user="frank")
mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "DialogType" -string "Server"',
runas="frank")
def test_write_default_boolean(self):
'''
Test writing a default setting
'''
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}):
macdefaults.write('com.apple.CrashReporter', 'Crash', True, type="boolean")
mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "Crash" -boolean "TRUE"',
runas=None)
def test_read_default(self):
'''
Test reading a default setting
'''
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {'cmd.run': mock}):
macdefaults.read('com.apple.CrashReporter', 'Crash')
mock.assert_called_once_with('defaults read "com.apple.CrashReporter" "Crash"', runas=None)
def test_read_default_with_user(self):
'''
Test reading a default setting as a specific user
'''
mock = MagicMock()
with patch.dict(macdefaults.__salt__, {'cmd.run': mock}):
macdefaults.read('com.apple.CrashReporter', 'Crash', user="frank")
mock.assert_called_once_with('defaults read "com.apple.CrashReporter" "Crash"', runas="frank")
if __name__ == '__main__':
from integration import run_tests
run_tests(MacDefaultsTestCase, needs_daemon=False)

View File

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
# Import Python libs
from __future__ import absolute_import
# Import Salt Libs
from salt.states import mac_defaults as macdefaults
# Import Salt Testing Libs
from salttesting import TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
MagicMock,
patch
)
ensure_in_syspath('../../')
macdefaults.__salt__ = {}
class MacDefaultsTestCase(TestCase):
def test_write(self):
'''
Test writing a default setting
'''
expected = {
'changes': {'written': 'com.apple.CrashReporter DialogType is set to Server'},
'comment': '',
'name': 'DialogType',
'result': True
}
read_mock = MagicMock(return_value='Local')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('DialogType', 'com.apple.CrashReporter', 'Server')
read_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', None)
write_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', 'Server', 'string', None)
self.assertEqual(out, expected)
def test_write_set(self):
'''
Test writing a default setting that is already set
'''
expected = {
'changes': {},
'comment': 'com.apple.CrashReporter DialogType is already set to Server',
'name': 'DialogType',
'result': True
}
read_mock = MagicMock(return_value='Server')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('DialogType', 'com.apple.CrashReporter', 'Server')
read_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', None)
assert not write_mock.called
self.assertEqual(out, expected)
def test_write_boolean(self):
'''
Test writing a default setting with a boolean
'''
expected = {
'changes': {'written': 'com.apple.something Key is set to True'},
'comment': '',
'name': 'Key',
'result': True
}
read_mock = MagicMock(return_value='0')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('Key', 'com.apple.something', True, vtype='boolean')
read_mock.assert_called_once_with('com.apple.something', 'Key', None)
write_mock.assert_called_once_with('com.apple.something', 'Key', True, 'boolean', None)
self.assertEqual(out, expected)
def test_write_boolean_match(self):
'''
Test writing a default setting with a boolean that is already set to the same value
'''
expected = {
'changes': {},
'comment': 'com.apple.something Key is already set to YES',
'name': 'Key',
'result': True
}
read_mock = MagicMock(return_value='1')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('Key', 'com.apple.something', 'YES', vtype='boolean')
read_mock.assert_called_once_with('com.apple.something', 'Key', None)
assert not write_mock.called
self.assertEqual(out, expected)
def test_write_integer(self):
'''
Test writing a default setting with a integer
'''
expected = {
'changes': {'written': 'com.apple.something Key is set to 1337'},
'comment': '',
'name': 'Key',
'result': True
}
read_mock = MagicMock(return_value='99')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('Key', 'com.apple.something', 1337, vtype='integer')
read_mock.assert_called_once_with('com.apple.something', 'Key', None)
write_mock.assert_called_once_with('com.apple.something', 'Key', 1337, 'integer', None)
self.assertEqual(out, expected)
def test_write_integer_match(self):
'''
Test writing a default setting with a integer that is already set to the same value
'''
expected = {
'changes': {},
'comment': 'com.apple.something Key is already set to 1337',
'name': 'Key',
'result': True
}
read_mock = MagicMock(return_value='1337')
write_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock,
'macdefaults.write': write_mock}):
out = macdefaults.write('Key', 'com.apple.something', 1337, vtype='integer')
read_mock.assert_called_once_with('com.apple.something', 'Key', None)
assert not write_mock.called
self.assertEqual(out, expected)
if __name__ == '__main__':
from integration import run_tests
run_tests(MacDefaultsTestCase, needs_daemon=False)