Skip to content

Commit

Permalink
Merge pull request #6508 from rca/file_managed_pillar_source_scheme
Browse files Browse the repository at this point in the history
Added pillar:// scheme to file.managed.
  • Loading branch information
thatch45 committed Aug 6, 2013
2 parents 3955194 + 74be66b commit d7aca29
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 13 deletions.
32 changes: 19 additions & 13 deletions salt/states/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ def managed(name,
show_diff=True,
create=True,
contents=None,
contents_pillar=None,
**kwargs):
'''
Manage a given file, this function allows for a file to be downloaded from
Expand Down Expand Up @@ -772,19 +773,13 @@ def managed(name,
contents of the file. Should not be used in conjunction with a source
file of any kind. Ignores hashes and does not use a templating engine.
Note, including a multiline string from an external source (such as
Pillar) presents a formatting challenege since the multiline content
will not adhere to YAML's required indentation. The external content
must be indented manually at the Jinja level::
/tmp/myfile:
file:
- managed
- contents: |
{{ salt['pillar.get']('some:multiline:text') | indent(8) }}
# Note the above example is indented by 8 spaces.
contents_pillar
Operates like ``contents``, but draws from a value stored in pillar,
using the pillar path syntax used in :mod:`pillar.get
<salt.modules.pillar.get>`. This is useful when the pillar value
contains newlines, as referencing a pillar variable using a jinja/mako
template can result in YAML formatting issues due to the newlines
causing indentation mismatches.
'''
# Make sure that leading zeros stripped by YAML loader are added back
mode = __salt__['config.manage_mode'](mode)
Expand Down Expand Up @@ -821,6 +816,17 @@ def managed(name,
return _error(
ret, 'Context must be formed as a dict')

if contents and contents_pillar:
return _error(
ret, 'Only one of contents and contents_pillar is permitted')

# If contents_pillar was used, get the pillar data
if contents_pillar:
contents = __salt__['pillar.get'](contents_pillar)
# Make sure file ends in newline
if not contents.endswith('\n'):
contents += '\n'

if not replace and os.path.exists(name):
# Check and set the permissions if necessary
ret, perms = __salt__['file.check_perms'](name,
Expand Down
60 changes: 60 additions & 0 deletions tests/unit/states/file_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,66 @@ def returner(contents, *args, **kwargs):
filestate.serialize('/tmp', dataset, formatter="json")
self.assertEquals(json.loads(returner.returned), dataset)

def test_contents_and_contents_pillar(self):
def returner(contents, *args, **kwargs):
returner.returned = contents
returner.returned = None

filestate.__salt__ = {
'file.manage_file': returner
}

manage_mode_mock = MagicMock()
filestate.__salt__['config.manage_mode'] = manage_mode_mock

ret = filestate.managed('/tmp/foo', contents='hi', contents_pillar='foo:bar')
self.assertEquals(False, ret['result'])

def test_contents_pillar_adds_newline(self):
# make sure the newline
pillar_value = 'i am the pillar value'
expected = '{}\n'.format(pillar_value)

self.run_contents_pillar(pillar_value, expected)

def test_contents_pillar_doesnt_add_more_newlines(self):
# make sure the newline
pillar_value = 'i am the pillar value\n'

self.run_contents_pillar(pillar_value, expected=pillar_value)

def run_contents_pillar(self, pillar_value, expected):
def returner(contents, *args, **kwargs):
returner.returned = (contents, args, kwargs)
returner.returned = None

filestate.__salt__ = {
'file.manage_file': returner
}

path = '/tmp/foo'
pillar_path = 'foo:bar'

# the values don't matter here
filestate.__salt__['config.manage_mode'] = MagicMock()
filestate.__salt__['file.source_list'] = MagicMock(return_value=[None, None])
filestate.__salt__['file.get_managed'] = MagicMock(return_value=[None, None, None])

# pillar.get should return the pillar_value
pillar_mock = MagicMock(return_value=pillar_value)
filestate.__salt__['pillar.get'] = pillar_mock

ret = filestate.managed(path, contents_pillar=pillar_path)

# make sure the pillar_mock is called with the given path
pillar_mock.assert_called_once_with(pillar_path)

# make sure no errors are returned
self.assertEquals(None, ret)

# make sure the value is correct
self.assertEquals(expected, returner.returned[1][-1])

if __name__ == '__main__':
from integration import run_tests
run_tests(TestFileState, needs_daemon=False)

0 comments on commit d7aca29

Please sign in to comment.