1# -*- coding: utf-8 -*- 2# Copyright (c) 2018 Ansible Project 3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 4 5# Make coding more python3-ish 6from __future__ import (absolute_import, division) 7__metaclass__ = type 8 9import json 10import os 11import shutil 12import tempfile 13 14import pytest 15 16from units.compat.mock import patch, MagicMock 17from ansible.module_utils._text import to_bytes 18 19from ansible.module_utils import basic 20 21 22class TestAnsibleModuleTmpDir: 23 24 DATA = ( 25 ( 26 { 27 "_ansible_tmpdir": "/path/to/dir", 28 "_ansible_remote_tmp": "/path/tmpdir", 29 "_ansible_keep_remote_files": False, 30 }, 31 True, 32 "/path/to/dir" 33 ), 34 ( 35 { 36 "_ansible_tmpdir": None, 37 "_ansible_remote_tmp": "/path/tmpdir", 38 "_ansible_keep_remote_files": False 39 }, 40 False, 41 "/path/tmpdir/ansible-moduletmp-42-" 42 ), 43 ( 44 { 45 "_ansible_tmpdir": None, 46 "_ansible_remote_tmp": "/path/tmpdir", 47 "_ansible_keep_remote_files": False 48 }, 49 True, 50 "/path/tmpdir/ansible-moduletmp-42-" 51 ), 52 ( 53 { 54 "_ansible_tmpdir": None, 55 "_ansible_remote_tmp": "$HOME/.test", 56 "_ansible_keep_remote_files": False 57 }, 58 False, 59 os.path.join(os.environ['HOME'], ".test/ansible-moduletmp-42-") 60 ), 61 ) 62 63 # pylint bug: https://github.com/PyCQA/pylint/issues/511 64 # pylint: disable=undefined-variable 65 @pytest.mark.parametrize('args, expected, stat_exists', ((s, e, t) for s, t, e in DATA)) 66 def test_tmpdir_property(self, monkeypatch, args, expected, stat_exists): 67 makedirs = {'called': False} 68 69 def mock_mkdtemp(prefix, dir): 70 return os.path.join(dir, prefix) 71 72 def mock_makedirs(path, mode): 73 makedirs['called'] = True 74 makedirs['path'] = path 75 makedirs['mode'] = mode 76 return 77 78 monkeypatch.setattr(tempfile, 'mkdtemp', mock_mkdtemp) 79 monkeypatch.setattr(os.path, 'exists', lambda x: stat_exists) 80 monkeypatch.setattr(os, 'makedirs', mock_makedirs) 81 monkeypatch.setattr(shutil, 'rmtree', lambda x: None) 82 monkeypatch.setattr(basic, '_ANSIBLE_ARGS', to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': args}))) 83 84 with patch('time.time', return_value=42): 85 am = basic.AnsibleModule(argument_spec={}) 86 actual_tmpdir = am.tmpdir 87 88 assert actual_tmpdir == expected 89 90 # verify subsequent calls always produces the same tmpdir 91 assert am.tmpdir == actual_tmpdir 92 93 if not stat_exists: 94 assert makedirs['called'] 95 expected = os.path.expanduser(os.path.expandvars(am._remote_tmp)) 96 assert makedirs['path'] == expected 97 assert makedirs['mode'] == 0o700 98 99 @pytest.mark.parametrize('stdin', ({"_ansible_tmpdir": None, 100 "_ansible_remote_tmp": "$HOME/.test", 101 "_ansible_keep_remote_files": True},), 102 indirect=['stdin']) 103 def test_tmpdir_makedirs_failure(self, am, monkeypatch): 104 105 mock_mkdtemp = MagicMock(return_value="/tmp/path") 106 mock_makedirs = MagicMock(side_effect=OSError("Some OS Error here")) 107 108 monkeypatch.setattr(tempfile, 'mkdtemp', mock_mkdtemp) 109 monkeypatch.setattr(os.path, 'exists', lambda x: False) 110 monkeypatch.setattr(os, 'makedirs', mock_makedirs) 111 112 actual = am.tmpdir 113 assert actual == "/tmp/path" 114 assert mock_makedirs.call_args[0] == (os.path.expanduser(os.path.expandvars("$HOME/.test")),) 115 assert mock_makedirs.call_args[1] == {"mode": 0o700} 116 117 # because makedirs failed the dir should be None so it uses the System tmp 118 assert mock_mkdtemp.call_args[1]['dir'] is None 119 assert mock_mkdtemp.call_args[1]['prefix'].startswith("ansible-moduletmp-") 120