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