2012-06-30 23:31:01 -07:00
|
|
|
import getpass
|
2012-06-30 16:56:02 -07:00
|
|
|
import os
|
2012-07-03 07:50:02 -07:00
|
|
|
import shutil
|
2012-06-30 16:56:02 -07:00
|
|
|
import sys
|
|
|
|
|
2020-04-10 08:30:25 +01:00
|
|
|
import pytest
|
2022-07-20 10:42:30 +01:00
|
|
|
|
2017-07-18 10:31:01 -06:00
|
|
|
import salt.utils.files
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-24 20:47:15 -05:00
|
|
|
import salt.utils.platform
|
2025-01-16 15:17:17 -07:00
|
|
|
import salt.utils.stringutils
|
2020-04-03 17:55:36 -03:00
|
|
|
from tests.support.case import ModuleCase
|
|
|
|
from tests.support.helpers import requires_system_grains
|
2018-12-07 17:52:49 +00:00
|
|
|
from tests.support.runtests import RUNTIME_VARS
|
2020-04-02 20:10:20 -05:00
|
|
|
|
2017-11-12 17:58:58 -08:00
|
|
|
# Posix only
|
|
|
|
try:
|
|
|
|
import grp
|
|
|
|
import pwd
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Windows only
|
|
|
|
try:
|
|
|
|
import win32file
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
2012-06-30 16:56:02 -07:00
|
|
|
|
2017-11-12 17:58:58 -08:00
|
|
|
def symlink(source, link_name):
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
2017-11-12 17:58:58 -08:00
|
|
|
Handle symlinks on Windows with Python < 3.2
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
2017-12-11 18:43:33 -05:00
|
|
|
if salt.utils.platform.is_windows():
|
2017-11-12 17:58:58 -08:00
|
|
|
win32file.CreateSymbolicLink(link_name, source)
|
|
|
|
else:
|
|
|
|
os.symlink(source, link_name)
|
|
|
|
|
2012-06-30 16:56:02 -07:00
|
|
|
|
2020-04-10 08:30:25 +01:00
|
|
|
@pytest.mark.windows_whitelisted
|
2017-04-03 17:04:09 +01:00
|
|
|
class FileModuleTest(ModuleCase):
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
2012-06-30 16:56:02 -07:00
|
|
|
Validate the file module
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
|
|
|
|
2012-06-30 16:56:02 -07:00
|
|
|
def setUp(self):
|
2018-12-07 17:52:49 +00:00
|
|
|
self.myfile = os.path.join(RUNTIME_VARS.TMP, "myfile")
|
2017-07-18 10:31:01 -06:00
|
|
|
with salt.utils.files.fopen(self.myfile, "w+") as fp:
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 10:30:33 -06:00
|
|
|
fp.write(salt.utils.stringutils.to_str("Hello" + os.linesep))
|
2018-12-07 17:52:49 +00:00
|
|
|
self.mydir = os.path.join(RUNTIME_VARS.TMP, "mydir/isawesome")
|
2012-08-25 14:21:38 +01:00
|
|
|
if not os.path.isdir(self.mydir):
|
|
|
|
# left behind... Don't fail because of this!
|
|
|
|
os.makedirs(self.mydir)
|
2018-12-07 17:52:49 +00:00
|
|
|
self.mysymlink = os.path.join(RUNTIME_VARS.TMP, "mysymlink")
|
2017-11-12 17:58:58 -08:00
|
|
|
if os.path.islink(self.mysymlink) or os.path.isfile(self.mysymlink):
|
2012-11-22 12:13:49 +01:00
|
|
|
os.remove(self.mysymlink)
|
2017-11-12 17:58:58 -08:00
|
|
|
symlink(self.myfile, self.mysymlink)
|
2018-12-07 17:52:49 +00:00
|
|
|
self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, "mybadsymlink")
|
2017-11-12 17:58:58 -08:00
|
|
|
if os.path.islink(self.mybadsymlink) or os.path.isfile(self.mybadsymlink):
|
2012-11-22 12:13:49 +01:00
|
|
|
os.remove(self.mybadsymlink)
|
2017-11-12 17:58:58 -08:00
|
|
|
symlink("/nonexistentpath", self.mybadsymlink)
|
2020-10-01 16:24:16 -07:00
|
|
|
super().setUp()
|
2012-06-30 16:56:02 -07:00
|
|
|
|
|
|
|
def tearDown(self):
|
2013-11-10 15:36:39 +00:00
|
|
|
if os.path.isfile(self.myfile):
|
|
|
|
os.remove(self.myfile)
|
2017-11-12 17:58:58 -08:00
|
|
|
if os.path.islink(self.mysymlink) or os.path.isfile(self.mysymlink):
|
2012-11-22 12:13:49 +01:00
|
|
|
os.remove(self.mysymlink)
|
2017-11-12 17:58:58 -08:00
|
|
|
if os.path.islink(self.mybadsymlink) or os.path.isfile(self.mybadsymlink):
|
2012-11-22 12:13:49 +01:00
|
|
|
os.remove(self.mybadsymlink)
|
2012-07-03 07:50:02 -07:00
|
|
|
shutil.rmtree(self.mydir, ignore_errors=True)
|
2020-10-01 16:24:16 -07:00
|
|
|
super().tearDown()
|
2012-06-30 16:56:02 -07:00
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No security context on Windows")
|
2020-04-03 17:55:36 -03:00
|
|
|
@requires_system_grains
|
|
|
|
def test_get_selinux_context(self, grains):
|
|
|
|
if grains.get("selinux", {}).get("enabled", False):
|
|
|
|
NEW_CONTEXT = "system_u:object_r:system_conf_t:s0"
|
|
|
|
self.run_function(
|
|
|
|
"file.set_selinux_context", arg=[self.myfile, *(NEW_CONTEXT.split(":"))]
|
|
|
|
)
|
|
|
|
ret_file = self.run_function("file.get_selinux_context", arg=[self.myfile])
|
|
|
|
self.assertEqual(ret_file, NEW_CONTEXT)
|
|
|
|
|
|
|
|
# Issue #56557. Ensure that the context of the directory
|
|
|
|
# containing one file is the context of the directory itself, and
|
|
|
|
# not the context of the first file in the directory.
|
|
|
|
self.run_function(
|
|
|
|
"file.set_selinux_context", arg=[self.mydir, *(NEW_CONTEXT.split(":"))]
|
|
|
|
)
|
|
|
|
ret_dir = self.run_function("file.get_selinux_context", arg=[self.mydir])
|
|
|
|
self.assertEqual(ret_dir, NEW_CONTEXT)
|
|
|
|
ret_updir = self.run_function(
|
|
|
|
"file.get_selinux_context",
|
|
|
|
arg=[os.path.abspath(os.path.join(self.mydir, ".."))],
|
|
|
|
)
|
|
|
|
self.assertNotEqual(ret_updir, NEW_CONTEXT)
|
|
|
|
else:
|
|
|
|
ret_file = self.run_function("file.get_selinux_context", arg=[self.myfile])
|
|
|
|
self.assertIn("No selinux context information is available", ret_file)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No security context on Windows")
|
2020-04-03 17:55:36 -03:00
|
|
|
@requires_system_grains
|
|
|
|
def test_set_selinux_context(self, grains):
|
|
|
|
if not grains.get("selinux", {}).get("enabled", False):
|
|
|
|
self.skipTest("selinux not available")
|
|
|
|
|
|
|
|
FILE_CONTEXT = "system_u:object_r:system_conf_t:s0"
|
|
|
|
ret_file = self.run_function(
|
|
|
|
"file.set_selinux_context", arg=[self.myfile, *(FILE_CONTEXT.split(":"))]
|
|
|
|
)
|
|
|
|
self.assertEqual(ret_file, FILE_CONTEXT)
|
|
|
|
|
|
|
|
DIR_CONTEXT = "system_u:object_r:user_home_t:s0"
|
|
|
|
ret_dir = self.run_function(
|
|
|
|
"file.set_selinux_context", arg=[self.mydir, *(DIR_CONTEXT.split(":"))]
|
|
|
|
)
|
|
|
|
self.assertEqual(ret_dir, DIR_CONTEXT)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chown(self):
|
2012-06-30 23:31:01 -07:00
|
|
|
user = getpass.getuser()
|
2012-06-30 17:33:34 -07:00
|
|
|
if sys.platform == "darwin":
|
|
|
|
group = "staff"
|
2014-09-04 10:23:59 +02:00
|
|
|
elif sys.platform.startswith(("linux", "freebsd", "openbsd")):
|
2013-05-03 11:47:08 +02:00
|
|
|
group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chown", arg=[self.myfile, user, group])
|
2012-06-30 17:33:34 -07:00
|
|
|
self.assertIsNone(ret)
|
|
|
|
fstat = os.stat(self.myfile)
|
2012-08-01 19:36:49 +01:00
|
|
|
self.assertEqual(fstat.st_uid, os.getuid())
|
|
|
|
self.assertEqual(fstat.st_gid, grp.getgrnam(group).gr_gid)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chown_no_user(self):
|
|
|
|
user = "notanyuseriknow"
|
2013-05-03 11:47:08 +02:00
|
|
|
group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chown", arg=[self.myfile, user, group])
|
2012-06-30 17:33:34 -07:00
|
|
|
self.assertIn("not exist", ret)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chown_no_user_no_group(self):
|
|
|
|
user = "notanyuseriknow"
|
|
|
|
group = "notanygroupyoushoulduse"
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chown", arg=[self.myfile, user, group])
|
2012-06-30 17:33:34 -07:00
|
|
|
self.assertIn("Group does not exist", ret)
|
|
|
|
self.assertIn("User does not exist", ret)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chown_no_path(self):
|
2012-06-30 23:31:01 -07:00
|
|
|
user = getpass.getuser()
|
2012-06-30 17:33:34 -07:00
|
|
|
if sys.platform == "darwin":
|
|
|
|
group = "staff"
|
2014-09-04 10:23:59 +02:00
|
|
|
elif sys.platform.startswith(("linux", "freebsd", "openbsd")):
|
2013-05-03 11:47:08 +02:00
|
|
|
group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name
|
2012-06-30 17:33:34 -07:00
|
|
|
ret = self.run_function("file.chown", arg=["/tmp/nosuchfile", user, group])
|
|
|
|
self.assertIn("File not found", ret)
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chown_noop(self):
|
|
|
|
user = ""
|
|
|
|
group = ""
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chown", arg=[self.myfile, user, group])
|
2012-06-30 17:33:34 -07:00
|
|
|
self.assertIsNone(ret)
|
|
|
|
fstat = os.stat(self.myfile)
|
2012-08-01 19:36:49 +01:00
|
|
|
self.assertEqual(fstat.st_uid, os.getuid())
|
|
|
|
self.assertEqual(fstat.st_gid, os.getgid())
|
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 16:56:02 -07:00
|
|
|
def test_chgrp(self):
|
|
|
|
if sys.platform == "darwin":
|
|
|
|
group = "everyone"
|
2014-09-04 10:23:59 +02:00
|
|
|
elif sys.platform.startswith(("linux", "freebsd", "openbsd")):
|
2013-05-03 11:47:08 +02:00
|
|
|
group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chgrp", arg=[self.myfile, group])
|
2012-06-30 16:56:02 -07:00
|
|
|
self.assertIsNone(ret)
|
|
|
|
fstat = os.stat(self.myfile)
|
2012-08-01 19:36:49 +01:00
|
|
|
self.assertEqual(fstat.st_gid, grp.getgrnam(group).gr_gid)
|
2012-06-30 17:33:34 -07:00
|
|
|
|
2022-12-04 21:21:09 +00:00
|
|
|
@pytest.mark.skip_on_windows(reason="No chgrp on Windows")
|
2012-06-30 17:33:34 -07:00
|
|
|
def test_chgrp_failure(self):
|
|
|
|
group = "thisgroupdoesntexist"
|
2012-08-01 19:36:49 +01:00
|
|
|
ret = self.run_function("file.chgrp", arg=[self.myfile, group])
|
2012-06-30 17:33:34 -07:00
|
|
|
self.assertIn("not exist", ret)
|
2012-07-03 07:50:02 -07:00
|
|
|
|
2012-10-17 14:06:17 -04:00
|
|
|
def test_patch(self):
|
|
|
|
if not self.run_function("cmd.has_exec", ["patch"]):
|
|
|
|
self.skipTest("patch is not installed")
|
|
|
|
|
2018-12-07 17:52:49 +00:00
|
|
|
src_patch = os.path.join(RUNTIME_VARS.FILES, "file", "base", "hello.patch")
|
|
|
|
src_file = os.path.join(RUNTIME_VARS.TMP, "src.txt")
|
2017-07-18 10:31:01 -06:00
|
|
|
with salt.utils.files.fopen(src_file, "w+") as fp:
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 10:30:33 -06:00
|
|
|
fp.write(salt.utils.stringutils.to_str("Hello\n"))
|
2012-10-17 14:06:17 -04:00
|
|
|
|
|
|
|
# dry-run should not modify src_file
|
|
|
|
ret = self.minion_run("file.patch", src_file, src_patch, dry_run=True)
|
|
|
|
assert ret["retcode"] == 0, repr(ret)
|
2017-07-18 10:31:01 -06:00
|
|
|
with salt.utils.files.fopen(src_file) as fp:
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 10:30:33 -06:00
|
|
|
self.assertEqual(salt.utils.stringutils.to_unicode(fp.read()), "Hello\n")
|
2012-10-17 14:06:17 -04:00
|
|
|
|
|
|
|
ret = self.minion_run("file.patch", src_file, src_patch)
|
|
|
|
assert ret["retcode"] == 0, repr(ret)
|
2017-07-18 10:31:01 -06:00
|
|
|
with salt.utils.files.fopen(src_file) as fp:
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 10:30:33 -06:00
|
|
|
self.assertEqual(
|
2025-01-16 15:17:17 -07:00
|
|
|
salt.utils.stringutils.to_unicode(fp.read()), f"Hello world{os.linesep}"
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 10:30:33 -06:00
|
|
|
)
|
2012-10-17 14:06:17 -04:00
|
|
|
|
2012-07-03 07:50:02 -07:00
|
|
|
def test_remove_file(self):
|
2013-11-10 15:36:11 +00:00
|
|
|
ret = self.run_function("file.remove", arg=[self.myfile])
|
2012-07-03 07:50:02 -07:00
|
|
|
self.assertTrue(ret)
|
|
|
|
|
|
|
|
def test_remove_dir(self):
|
2013-11-10 15:36:11 +00:00
|
|
|
ret = self.run_function("file.remove", arg=[self.mydir])
|
2012-07-03 07:50:02 -07:00
|
|
|
self.assertTrue(ret)
|
|
|
|
|
2012-11-22 12:13:49 +01:00
|
|
|
def test_remove_symlink(self):
|
2013-11-10 15:36:11 +00:00
|
|
|
ret = self.run_function("file.remove", arg=[self.mysymlink])
|
2012-11-22 12:13:49 +01:00
|
|
|
self.assertTrue(ret)
|
|
|
|
|
|
|
|
def test_remove_broken_symlink(self):
|
2013-11-10 15:36:11 +00:00
|
|
|
ret = self.run_function("file.remove", arg=[self.mybadsymlink])
|
2012-11-22 12:13:49 +01:00
|
|
|
self.assertTrue(ret)
|
|
|
|
|
2012-07-03 07:50:02 -07:00
|
|
|
def test_cannot_remove(self):
|
2013-11-10 15:30:42 +00:00
|
|
|
ret = self.run_function("file.remove", arg=["tty"])
|
2012-07-03 07:50:02 -07:00
|
|
|
self.assertEqual(
|
2015-06-29 12:27:22 -06:00
|
|
|
"ERROR executing 'file.remove': File path must be absolute: tty", ret
|
2012-08-01 19:36:49 +01:00
|
|
|
)
|
2012-07-20 12:21:01 +06:00
|
|
|
|
2013-06-30 23:19:16 +10:00
|
|
|
def test_source_list_for_single_file_returns_unchanged(self):
|
|
|
|
ret = self.run_function(
|
|
|
|
"file.source_list", ["salt://http/httpd.conf", "filehash", "base"]
|
|
|
|
)
|
Python 3 Fixes (Pt. 2) (#39397)
* Typo in comment
* First convert to string if not already a string. Then to bytes under Py3.
The reason being that jids from the CLI, at least the one fed in
integration.runners.jobs.ManageTest.test_loopup_jid is loaded as an
integer, and, while the Py2 code converts JIDs to strings, under Py3, we
assume JID's are already strings.
* Mark tests which require root permissions to run
* Allow declaring that the function IS a class method.
```
Python 3.5.3 (default, Jan 21 2017, 00:29:12)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def bar(self):
... print('bar')
...
>>> import inspect
>>> inspect.ismethod(Foo.bar)
False
>>> inspect.ismethod(Foo().bar)
True
```
On Python 2, `inspect.ismethod` returns `True` for bound and unbound
methods while on Python 3 it only returns `True` for bound methods.
The explicit `is_class_method` is to avoid instantiating the class just
to get the function signature.
* Always decode responses to the Python version native string implementation
* Just compare the objects as matching list.
Asserting same item count doesn't make that much sense.
* Py3 compatibility
* Fix saltnado tests under Py3
* Python 3 compatibility
* Show me the full traceback
* Revert "Convert fileserver data from bytes to strings"
This reverts commit e53972f8c6464c0fdeffb875c785b3ce530c7c5e.
* Revert "Under Py3, we get `bytes` when using the roots backend directly"
This reverts commit 9f73b240c1d68d785a04d898fab84837c154a5ae.
* Convert from bytes to str if not a binary file
* Py3 compatibility fixes.
Convert file contents from bytes to string if not a binary file
2017-02-14 23:20:56 +00:00
|
|
|
self.assertEqual(list(ret), ["salt://http/httpd.conf", "filehash"])
|
2013-06-30 23:19:16 +10:00
|
|
|
|
2015-11-17 15:29:46 -08:00
|
|
|
def test_source_list_for_single_local_file_slash_returns_unchanged(self):
|
|
|
|
ret = self.run_function("file.source_list", [self.myfile, "filehash", "base"])
|
Python 3 Fixes (Pt. 2) (#39397)
* Typo in comment
* First convert to string if not already a string. Then to bytes under Py3.
The reason being that jids from the CLI, at least the one fed in
integration.runners.jobs.ManageTest.test_loopup_jid is loaded as an
integer, and, while the Py2 code converts JIDs to strings, under Py3, we
assume JID's are already strings.
* Mark tests which require root permissions to run
* Allow declaring that the function IS a class method.
```
Python 3.5.3 (default, Jan 21 2017, 00:29:12)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def bar(self):
... print('bar')
...
>>> import inspect
>>> inspect.ismethod(Foo.bar)
False
>>> inspect.ismethod(Foo().bar)
True
```
On Python 2, `inspect.ismethod` returns `True` for bound and unbound
methods while on Python 3 it only returns `True` for bound methods.
The explicit `is_class_method` is to avoid instantiating the class just
to get the function signature.
* Always decode responses to the Python version native string implementation
* Just compare the objects as matching list.
Asserting same item count doesn't make that much sense.
* Py3 compatibility
* Fix saltnado tests under Py3
* Python 3 compatibility
* Show me the full traceback
* Revert "Convert fileserver data from bytes to strings"
This reverts commit e53972f8c6464c0fdeffb875c785b3ce530c7c5e.
* Revert "Under Py3, we get `bytes` when using the roots backend directly"
This reverts commit 9f73b240c1d68d785a04d898fab84837c154a5ae.
* Convert from bytes to str if not a binary file
* Py3 compatibility fixes.
Convert file contents from bytes to string if not a binary file
2017-02-14 23:20:56 +00:00
|
|
|
self.assertEqual(list(ret), [self.myfile, "filehash"])
|
2015-11-17 15:29:46 -08:00
|
|
|
|
|
|
|
def test_source_list_for_single_local_file_proto_returns_unchanged(self):
|
|
|
|
ret = self.run_function(
|
|
|
|
"file.source_list", ["file://" + self.myfile, "filehash", "base"]
|
|
|
|
)
|
Python 3 Fixes (Pt. 2) (#39397)
* Typo in comment
* First convert to string if not already a string. Then to bytes under Py3.
The reason being that jids from the CLI, at least the one fed in
integration.runners.jobs.ManageTest.test_loopup_jid is loaded as an
integer, and, while the Py2 code converts JIDs to strings, under Py3, we
assume JID's are already strings.
* Mark tests which require root permissions to run
* Allow declaring that the function IS a class method.
```
Python 3.5.3 (default, Jan 21 2017, 00:29:12)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def bar(self):
... print('bar')
...
>>> import inspect
>>> inspect.ismethod(Foo.bar)
False
>>> inspect.ismethod(Foo().bar)
True
```
On Python 2, `inspect.ismethod` returns `True` for bound and unbound
methods while on Python 3 it only returns `True` for bound methods.
The explicit `is_class_method` is to avoid instantiating the class just
to get the function signature.
* Always decode responses to the Python version native string implementation
* Just compare the objects as matching list.
Asserting same item count doesn't make that much sense.
* Py3 compatibility
* Fix saltnado tests under Py3
* Python 3 compatibility
* Show me the full traceback
* Revert "Convert fileserver data from bytes to strings"
This reverts commit e53972f8c6464c0fdeffb875c785b3ce530c7c5e.
* Revert "Under Py3, we get `bytes` when using the roots backend directly"
This reverts commit 9f73b240c1d68d785a04d898fab84837c154a5ae.
* Convert from bytes to str if not a binary file
* Py3 compatibility fixes.
Convert file contents from bytes to string if not a binary file
2017-02-14 23:20:56 +00:00
|
|
|
self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"])
|
2013-06-24 23:53:59 +01:00
|
|
|
|
2020-10-01 16:24:16 -07:00
|
|
|
def test_source_list_for_multiple_files_with_missing_files(self):
|
|
|
|
file_list = [
|
|
|
|
"salt://does/not/exist",
|
|
|
|
"file://" + self.myfile,
|
|
|
|
"http://localhost//does/not/exist",
|
|
|
|
"salt://http/httpd.conf",
|
|
|
|
]
|
|
|
|
ret = self.run_function("file.source_list", [file_list, "filehash", "base"])
|
|
|
|
self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"])
|
|
|
|
|
|
|
|
def test_source_list_for_multiple_files_dict_with_missing_files(self):
|
|
|
|
file_list = [
|
|
|
|
{"salt://does/not/exist": "filehash"},
|
|
|
|
{"file://" + self.myfile: "filehash"},
|
|
|
|
{"http://localhost//does/not/exist": "filehash"},
|
|
|
|
{"salt://http/httpd.conf": "filehash"},
|
|
|
|
]
|
|
|
|
ret = self.run_function("file.source_list", [file_list, "", "base"])
|
|
|
|
self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"])
|
|
|
|
|
2017-11-04 09:33:31 -07:00
|
|
|
def test_file_line_changes_format(self):
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
2017-11-04 09:33:31 -07:00
|
|
|
Test file.line changes output formatting.
|
|
|
|
|
|
|
|
Issue #41474
|
2020-04-02 20:10:20 -05:00
|
|
|
"""
|
2017-11-04 09:33:31 -07:00
|
|
|
ret = self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye", mode="insert", after="Hello"
|
|
|
|
)
|
2017-11-07 00:30:48 -08:00
|
|
|
self.assertIn("Hello" + os.linesep + "+Goodbye", ret)
|
2017-11-04 09:33:31 -07:00
|
|
|
|
2020-04-13 11:46:34 -05:00
|
|
|
def test_file_line_changes_entire_line(self):
|
|
|
|
"""
|
|
|
|
Test file.line entire line matching
|
|
|
|
|
|
|
|
Issue #49855
|
|
|
|
"""
|
|
|
|
ret = self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye", mode="insert", after="Hello"
|
|
|
|
)
|
|
|
|
assert "Hello" + os.linesep + "+Goodbye" in ret
|
|
|
|
|
|
|
|
ret = self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye 1", mode="insert", after="Hello"
|
|
|
|
)
|
|
|
|
assert (
|
|
|
|
"Hello" + os.linesep + "+Goodbye 1" + os.linesep + " Goodbye" + os.linesep
|
|
|
|
in ret
|
|
|
|
)
|
|
|
|
|
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fh_:
|
|
|
|
content = fh_.read()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Hello" + os.linesep + "Goodbye 1" + os.linesep + "Goodbye" + os.linesep
|
|
|
|
== content
|
|
|
|
)
|
|
|
|
|
2017-11-12 18:07:50 -08:00
|
|
|
def test_file_line_content(self):
|
|
|
|
self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye", mode="insert", after="Hello"
|
|
|
|
)
|
2017-12-11 18:43:33 -05:00
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fp:
|
2017-11-12 18:07:50 -08:00
|
|
|
content = fp.read()
|
|
|
|
self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep)
|
2018-11-02 23:02:04 +01:00
|
|
|
|
|
|
|
def test_file_line_duplicate_insert_after(self):
|
|
|
|
"""
|
|
|
|
Test file.line duplicates line.
|
|
|
|
|
|
|
|
Issue #50254
|
|
|
|
"""
|
|
|
|
with salt.utils.files.fopen(self.myfile, "a") as fp:
|
|
|
|
fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep))
|
|
|
|
self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye", mode="insert", after="Hello"
|
|
|
|
)
|
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fp:
|
|
|
|
content = fp.read()
|
|
|
|
self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep)
|
|
|
|
|
|
|
|
def test_file_line_duplicate_insert_before(self):
|
|
|
|
"""
|
|
|
|
Test file.line duplicates line.
|
|
|
|
|
|
|
|
Issue #50254
|
|
|
|
"""
|
|
|
|
with salt.utils.files.fopen(self.myfile, "a") as fp:
|
|
|
|
fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep))
|
|
|
|
self.minion_run(
|
|
|
|
"file.line", self.myfile, "Hello", mode="insert", before="Goodbye"
|
|
|
|
)
|
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fp:
|
|
|
|
content = fp.read()
|
|
|
|
self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep)
|
|
|
|
|
|
|
|
def test_file_line_duplicate_ensure_after(self):
|
|
|
|
"""
|
|
|
|
Test file.line duplicates line.
|
|
|
|
|
|
|
|
Issue #50254
|
|
|
|
"""
|
|
|
|
with salt.utils.files.fopen(self.myfile, "a") as fp:
|
|
|
|
fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep))
|
|
|
|
self.minion_run(
|
|
|
|
"file.line", self.myfile, "Goodbye", mode="ensure", after="Hello"
|
|
|
|
)
|
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fp:
|
|
|
|
content = fp.read()
|
|
|
|
self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep)
|
|
|
|
|
|
|
|
def test_file_line_duplicate_ensure_before(self):
|
|
|
|
"""
|
|
|
|
Test file.line duplicates line.
|
|
|
|
|
|
|
|
Issue #50254
|
|
|
|
"""
|
|
|
|
with salt.utils.files.fopen(self.myfile, "a") as fp:
|
|
|
|
fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep))
|
|
|
|
self.minion_run(
|
|
|
|
"file.line", self.myfile, "Hello", mode="ensure", before="Goodbye"
|
|
|
|
)
|
|
|
|
with salt.utils.files.fopen(self.myfile, "r") as fp:
|
|
|
|
content = fp.read()
|
|
|
|
self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep)
|
2020-07-27 14:33:25 -07:00
|
|
|
|
|
|
|
def test_file_read_bytes(self):
|
|
|
|
"""
|
|
|
|
Test that ``file.read`` reads and returns ``bytes`` data
|
|
|
|
"""
|
|
|
|
# Write some random bytes
|
|
|
|
data = b"n\x1a\xf7S@tBI\xa9J"
|
|
|
|
with salt.utils.files.fopen(self.myfile, "wb") as fp:
|
|
|
|
fp.write(data)
|
|
|
|
|
|
|
|
ret = self.minion_run("file.read", self.myfile, binary=True)
|
|
|
|
self.assertEqual(type(ret), bytes)
|
|
|
|
self.assertEqual(ret, data)
|
|
|
|
|
|
|
|
def test_file_read_str(self):
|
|
|
|
"""
|
|
|
|
Test that ``file.read`` reads and returns ``str`` data
|
|
|
|
"""
|
|
|
|
# Write some str data
|
|
|
|
data = "printable characters"
|
|
|
|
with salt.utils.files.fopen(self.myfile, "w") as fp:
|
|
|
|
fp.write(data)
|
|
|
|
|
|
|
|
ret = self.minion_run("file.read", self.myfile)
|
|
|
|
self.assertEqual(type(ret), str)
|
|
|
|
self.assertEqual(ret, data)
|