File: test_task_remote_mgr.py

package info (click to toggle)
cylc-flow 8.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 14,368 kB
  • sloc: python: 87,751; sh: 17,109; sql: 233; xml: 171; javascript: 78; lisp: 55; makefile: 11
file content (167 lines) | stat: -rw-r--r-- 6,391 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import cylc
from cylc.flow.task_remote_mgr import (
    REMOTE_FILE_INSTALL_DONE,
    REMOTE_FILE_INSTALL_FAILED
)


async def test_remote_tidy(
    flow,
    scheduler,
    start,
    mock_glbl_cfg,
    one_conf,
    monkeypatch
):
    """Remote tidy gets platforms for install targets.

    In particular, referencing https://github.com/cylc/cylc-flow/issues/5429,
    ensure that install targets defined implicitly by platform name are found.

    Mock remote init map:
        - Include an install target (quiz) with
          message != REMOTE_FILE_INSTALL_DONE to ensure that
          this is picked out.
        - Install targets where we can get a platform
          - foo - Install target is implicitly the platfrom name.
          - bar9 - The install target is implicitly the plaform name,
            and the platform name matches a platform regex.
          - baz - Install target is set explicitly.
        - An install target (qux) where we cannot get a platform: Ensure
          that we get the desired error.

    Test that platforms with no good hosts (no host not in bad hosts).
    """
    # Monkeypatch away subprocess.Popen calls - prevent any interaction with
    # remotes actually happening:
    class MockProc:
        def __init__(self, *args, **kwargs):
            self.poll = lambda: True
            if (
                'baum' in args[0]
                or 'bay' in args[0]
            ):
                self.returncode = 255
            else:
                self.returncode = 0
            self.communicate = lambda: ('out', 'err')

    monkeypatch.setattr(
        cylc.flow.task_remote_mgr,
        'Popen',
        lambda *args, **kwargs: MockProc(*args, **kwargs)
    )

    # Monkeypath function to add a sort order which we don't need in the
    # real code but rely to prevent the test becoming flaky:
    def mock_get_install_target_platforms_map(*args, **kwargs):
        """Add sort to original function to ensure test consistency"""
        from cylc.flow.platforms import get_install_target_to_platforms_map
        result = get_install_target_to_platforms_map(*args, **kwargs)
        sorted_result = {}
        for key in sorted(result):
            sorted_result[key] = sorted(
                result[key], key=lambda x: x['name'], reverse=True)
        return sorted_result

    monkeypatch.setattr(
        cylc.flow.task_remote_mgr,
        'get_install_target_to_platforms_map',
        mock_get_install_target_platforms_map
    )

    # Set up global config
    mock_glbl_cfg(
        'cylc.flow.platforms.glbl_cfg',
        '''
            [platforms]
                [[foo]]
                    # install target = foo  (implicit)
                    # hosts = foo  (implicit)
                [[bar.]]
                    # install target = bar1 to bar9 (implicit)
                    # hosts = bar1 to bar9 (implicit)
                [[baz]]
                    install target = baz
                    # baum and bay should be uncontactable:
                    hosts = baum, bay, baz
                    [[[selection]]]
                        method = definition order
                [[notthisone]]
                    install target = baz
                    hosts = baum, bay
                [[bay]]
        ''',
    )

    # Get a scheduler:
    id_ = flow(one_conf)
    schd = scheduler(id_)
    async with start(schd) as log:
        # Write database with 6 tasks using 3 platforms:
        platforms = ['baz', 'bar9', 'foo', 'notthisone', 'bay']
        line = r"('', '', {}, 0, 1, '', '', 0,'', '', '', 0, '', '{}', 4, '')"
        stmt = r"INSERT INTO task_jobs VALUES" + r','.join([
            line.format(i, platform) for i, platform in enumerate(platforms)
        ])
        schd.workflow_db_mgr.pri_dao.connect().execute(stmt)
        schd.workflow_db_mgr.pri_dao.connect().commit()

        # Mock a remote init map.
        schd.task_job_mgr.task_remote_mgr.remote_init_map = {
            'baz': REMOTE_FILE_INSTALL_DONE,      # Should match platform baz
            'bar9': REMOTE_FILE_INSTALL_DONE,     # Should match platform bar.
            'foo': REMOTE_FILE_INSTALL_DONE,      # Should match plaform foo
            'qux': REMOTE_FILE_INSTALL_DONE,      # Should not match a plaform
            'quiz': REMOTE_FILE_INSTALL_FAILED,   # Should not be considered
            'bay': REMOTE_FILE_INSTALL_DONE,      # Should return NoPlatforms
        }

        # Clear the log, run the test:
        log.clear()
        schd.task_job_mgr.task_remote_mgr.bad_hosts.update(['baum', 'bay'])
        schd.task_job_mgr.task_remote_mgr.remote_tidy()
        pass

    records = [str(r.msg) for r in log.records]

    # We can't get qux, no defined platform has a matching install target:
    qux_msg = 'No platforms available to remote tidy install targets:\n * qux'
    assert qux_msg in records

    # We can get foo bar baz, and we try to remote tidy them.
    # (This will ultimately fail, but past the point we are testing).
    for target in ['foo', 'bar9', 'baz']:
        msg = f'platform: {target} - remote tidy (on {target})'
        assert msg in records

    # We haven't done anything with Quiz because we're only looking
    # at cases where platform == REMOTE_FILE_INSTALL_DONE
    assert not [r for r in records if 'quiz' in r]

    notthisone_msg = (
        'platform: notthisone - clean up did not complete'
        '\nUnable to find valid host for notthisone'
    )
    assert notthisone_msg in records

    bay_msg = (
        'Unable to find a platform from install target'
        ' bay during remote tidy.')
    assert bay_msg in records