Browse Source

Wire in get_dlrn_hash

This wires in the get_dlrn_hash function to the release dictionary,
so that we end up with an actual dlrn hash instead of a named hash.

Release dictionary tests updated to mock this function, including
verification of the expected calls to get_dlrn_hash.

This also adds more comprehensive logging and unittests to the
get_dlrn_hash function.

Change-Id: I41087bf0c247b933a641ea0da582b5f2b368840e
John Trowbridge 11 months ago
parent
commit
6f06040bad

+ 45
- 12
scripts/emit_releases_file/emit_releases_file.py View File

@@ -11,6 +11,11 @@ RELEASES = ['newton', 'ocata', 'pike', 'queens', 'master']
11 11
 # Define long term releases
12 12
 LONG_TERM_SUPPORT_RELEASES = ['queens']
13 13
 
14
+# NAMED DLRN HASHES
15
+NEWTON_HASH_NAME = 'current-passed-ci'
16
+CURRENT_HASH_NAME = 'current-tripleo'
17
+PREVIOUS_HASH_NAME = 'previous-current-tripleo'
18
+
14 19
 
15 20
 def get_relative_release(release, relative_idx):
16 21
     current_idx = RELEASES.index(release)
@@ -40,30 +45,41 @@ def load_featureset_file(featureset_file):
40 45
     return featureset
41 46
 
42 47
 
43
-def get_dlrn_hash(release, hash_name, retries=10):
48
+def get_dlrn_hash(release, hash_name, retries=10, timeout=4):
44 49
     logger = logging.getLogger('emit-releases')
45 50
     full_hash_pattern = re.compile('[a-z,0-9]{40}_[a-z,0-9]{8}')
46 51
     repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
47 52
                 (release, hash_name))
48 53
     for retry_num in range(retries):
49 54
         repo_file = None
50
-        # Timeout if initial connection is longer than default
51
-        # TCP packet retransmission window (3 secs), or if the
52
-        # sending of the data takes more than 27 seconds.
53 55
         try:
54
-            repo_file = requests.get(repo_url, timeout=(3.05, 27))
56
+            repo_file = requests.get(repo_url, timeout=timeout)
55 57
         except Exception as e:
58
+            logger.warning("Attempt {} of {} to get DLRN hash threw an "
59
+                           "exception.".format(retry_num + 1, retries))
56 60
             logger.exception(e)
57 61
             pass
58 62
         else:
59 63
             if repo_file is not None and repo_file.ok:
60 64
                 break
61 65
 
66
+            elif repo_file:
67
+                logger.warning("Attempt {} of {} to get DLRN hash returned "
68
+                               "status code {}.".format(retry_num + 1,
69
+                                                        retries,
70
+                                                        repo_file.status_code))
71
+            else:
72
+                logger.warning("Attempt {} of {} to get DLRN hash failed to "
73
+                               "get a response.".format(retry_num + 1,
74
+                                                        retries))
75
+
62 76
     if repo_file is None or not repo_file.ok:
63 77
         raise RuntimeError("Failed to retrieve repo file from {} after "
64 78
                            "{} retries".format(repo_url, retries))
65 79
 
66
-    full_hash = full_hash_pattern.findall(repo_file.content)
80
+    full_hash = full_hash_pattern.findall(repo_file.text)
81
+    logger.info("Got DLRN hash: {} for the named hash: {} on the {} "
82
+                "release".format(full_hash[0], hash_name, release))
67 83
     return full_hash[0]
68 84
 
69 85
 
@@ -100,37 +116,54 @@ def compose_releases_dictionary(stable_release, featureset):
100 116
             "used in a fast forward upgrade. Current long-term support "
101 117
             "releases:  {}".format(stable_release, LONG_TERM_SUPPORT_RELEASES))
102 118
 
119
+    if stable_release == 'newton':
120
+        current_hash = get_dlrn_hash(stable_release, NEWTON_HASH_NAME)
121
+    else:
122
+        current_hash = get_dlrn_hash(stable_release, CURRENT_HASH_NAME)
123
+
103 124
     releases_dictionary = {
104 125
         'undercloud_install_release': stable_release,
105
-        'undercloud_install_hash': 'current-tripleo',
126
+        'undercloud_install_hash': current_hash,
106 127
         'undercloud_target_release': stable_release,
107
-        'undercloud_target_hash': 'current-tripleo',
128
+        'undercloud_target_hash': current_hash,
108 129
         'overcloud_deploy_release': stable_release,
109
-        'overcloud_deploy_hash': 'current-tripleo',
130
+        'overcloud_deploy_hash': current_hash,
110 131
         'overcloud_target_release': stable_release,
111
-        'overcloud_target_hash': 'current-tripleo'
132
+        'overcloud_target_hash': current_hash
112 133
     }
113 134
 
114 135
     if featureset.get('mixed_upgrade'):
115 136
         if featureset.get('overcloud_upgrade'):
116 137
             logger.info('Doing an overcloud upgrade')
117 138
             deploy_release = get_relative_release(stable_release, -1)
139
+            if deploy_release == 'newton':
140
+                deploy_hash = get_dlrn_hash(deploy_release, NEWTON_HASH_NAME)
141
+            else:
142
+                deploy_hash = get_dlrn_hash(deploy_release, CURRENT_HASH_NAME)
118 143
             releases_dictionary['overcloud_deploy_release'] = deploy_release
144
+            releases_dictionary['overcloud_deploy_hash'] = deploy_hash
119 145
 
120 146
         elif featureset.get('ffu_overcloud_upgrade'):
121 147
             logger.info('Doing an overcloud fast forward upgrade')
122 148
             deploy_release = get_relative_release(stable_release, -3)
149
+            if deploy_release == 'newton':
150
+                deploy_hash = get_dlrn_hash(deploy_release, NEWTON_HASH_NAME)
151
+            else:
152
+                deploy_hash = get_dlrn_hash(deploy_release, CURRENT_HASH_NAME)
123 153
             releases_dictionary['overcloud_deploy_release'] = deploy_release
154
+            releases_dictionary['overcloud_deploy_hash'] = deploy_hash
124 155
 
125 156
     elif featureset.get('undercloud_upgrade'):
126 157
         logger.info('Doing an undercloud upgrade')
127 158
         install_release = get_relative_release(stable_release, -1)
159
+        install_hash = get_dlrn_hash(install_release, CURRENT_HASH_NAME)
128 160
         releases_dictionary['undercloud_install_release'] = install_release
161
+        releases_dictionary['undercloud_install_hash'] = install_hash
129 162
 
130 163
     elif featureset.get('overcloud_update'):
131 164
         logger.info('Doing an overcloud update')
132
-        releases_dictionary['overcloud_deploy_hash'] = \
133
-            'previous-current-tripleo'
165
+        previous_hash = get_dlrn_hash(stable_release, PREVIOUS_HASH_NAME)
166
+        releases_dictionary['overcloud_deploy_hash'] = previous_hash
134 167
 
135 168
     logger.debug("stable_release: %s, featureset: %s", stable_release,
136 169
                  featureset)

+ 137
- 10
scripts/emit_releases_file/test_get_dlrn_hash.py View File

@@ -4,28 +4,53 @@ import mock
4 4
 import pytest
5 5
 
6 6
 
7
+@mock.patch('logging.getLogger')
7 8
 @mock.patch('requests.get')
8
-def test_get_dlrn_hash(mock_get):
9
+def test_get_dlrn_hash_ok(mock_get, mock_logging):
10
+    mock_logger = mock.MagicMock()
11
+    mock_logging.return_value = mock_logger
12
+    mock_log_exception = mock.MagicMock()
13
+    mock_log_warning = mock.MagicMock()
14
+    mock_log_info = mock.MagicMock()
15
+    mock_logger.exception = mock_log_exception
16
+    mock_logger.warning = mock_log_warning
17
+    mock_logger.info = mock_log_info
9 18
     mock_response = mock.Mock()
10
-    mock_response.content = (
19
+    mock_response.ok = True
20
+    mock_response.text = (
11 21
         '[delorean]\nname=delorean-openstack-nova-81c23c04'
12 22
         '7e8e0fc03b54164921f49fdb4103202c\nbaseurl=https:/'
13 23
         '/trunk.rdoproject.org/centos7/81/c2/81c23c047e8e0'
14 24
         'fc03b54164921f49fdb4103202c_b333f915\nenabled=1\n'
15 25
         'gpgcheck=0\npriority=1')
26
+    mock_get.return_value = mock_response
16 27
     release = 'master'
17 28
     hash_name = 'current-tripleo'
29
+    dlrn_hash = '81c23c047e8e0fc03b54164921f49fdb4103202c_b333f915'
18 30
     repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
19 31
                 (release, hash_name))
20
-    mock_get.return_value = mock_response
21
-    assert (get_dlrn_hash(
22
-        release,
23
-        hash_name) == '81c23c047e8e0fc03b54164921f49fdb4103202c_b333f915')
24
-    mock_get.assert_called_once_with(repo_url, timeout=(3.05, 27))
32
+    assert get_dlrn_hash(release, hash_name) == dlrn_hash
33
+    mock_get.assert_called_once_with(repo_url, timeout=4)
34
+    mock_log_info.assert_called_once_with("Got DLRN hash: {} for the named "
35
+                                          "hash: {} on the {} "
36
+                                          "release".format(dlrn_hash,
37
+                                                           hash_name,
38
+                                                           release))
39
+    mock_log_warning.assert_not_called()
40
+    mock_log_exception.assert_not_called()
25 41
 
26 42
 
43
+@mock.patch('logging.getLogger')
27 44
 @mock.patch('requests.get')
28
-def test_null_response_raises_runtimeerror(mock_get):
45
+def test_null_response_raises_runtimeerror(mock_get, mock_logging):
46
+    mock_logger = mock.MagicMock()
47
+    mock_logging.return_value = mock_logger
48
+    mock_log_exception = mock.MagicMock()
49
+    mock_log_warning = mock.MagicMock()
50
+    mock_log_info = mock.MagicMock()
51
+    mock_logger.exception = mock_log_exception
52
+    mock_logger.warning = mock_log_warning
53
+    mock_logger.info = mock_log_info
29 54
     release = 'master'
30 55
     hash_name = 'current-tripleo'
31 56
     repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
@@ -33,5 +58,107 @@ def test_null_response_raises_runtimeerror(mock_get):
33 58
     mock_get.return_value = None
34 59
     with pytest.raises(RuntimeError):
35 60
         get_dlrn_hash(release, hash_name)
36
-    mock_get.assert_called_with(repo_url, timeout=(3.05, 27))
37
-    assert (10 == mock_get.call_count)
61
+    mock_get.assert_called_with(repo_url, timeout=4)
62
+    assert mock_get.call_count == 10
63
+    mock_log_info.assert_not_called()
64
+    mock_log_warning.assert_called_with("Attempt 10 of 10 to get DLRN hash "
65
+                                        "failed to get a response.")
66
+    assert mock_log_warning.call_count == 10
67
+    mock_log_exception.assert_not_called()
68
+
69
+
70
+@mock.patch('logging.getLogger')
71
+@mock.patch('requests.get')
72
+def test_get_dlrn_hash_500_then_200(mock_get, mock_logging):
73
+    mock_logger = mock.MagicMock()
74
+    mock_logging.return_value = mock_logger
75
+    mock_log_exception = mock.MagicMock()
76
+    mock_log_warning = mock.MagicMock()
77
+    mock_log_info = mock.MagicMock()
78
+    mock_logger.exception = mock_log_exception
79
+    mock_logger.warning = mock_log_warning
80
+    mock_logger.info = mock_log_info
81
+    mock_response_ok = mock.Mock()
82
+    mock_response_ok.ok = True
83
+    mock_response_ok.text = (
84
+        '[delorean]\nname=delorean-openstack-nova-81c23c04'
85
+        '7e8e0fc03b54164921f49fdb4103202c\nbaseurl=https:/'
86
+        '/trunk.rdoproject.org/centos7/81/c2/81c23c047e8e0'
87
+        'fc03b54164921f49fdb4103202c_b333f915\nenabled=1\n'
88
+        'gpgcheck=0\npriority=1')
89
+    mock_response_bad = mock.Mock()
90
+    mock_response_bad.ok = False
91
+    mock_response_bad.status_code = 500
92
+    release = 'master'
93
+    hash_name = 'current-tripleo'
94
+    dlrn_hash = '81c23c047e8e0fc03b54164921f49fdb4103202c_b333f915'
95
+    repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
96
+                (release, hash_name))
97
+    mock_get.side_effect = [mock_response_bad, mock_response_ok]
98
+    assert get_dlrn_hash(release, hash_name, retries=10) == dlrn_hash
99
+    mock_get.assert_called_with(repo_url, timeout=4)
100
+    mock_log_info.assert_called_once_with("Got DLRN hash: {} for the named "
101
+                                          "hash: {} on the {} "
102
+                                          "release".format(dlrn_hash,
103
+                                                           hash_name,
104
+                                                           release))
105
+    mock_log_warning.assert_called_once_with("Attempt 1 of 10 to get DLRN "
106
+                                             "hash returned status code 500.")
107
+    mock_log_exception.assert_not_called()
108
+
109
+
110
+@mock.patch('logging.getLogger')
111
+@mock.patch('requests.get')
112
+def test_get_dlrn_hash_timeout(mock_get, mock_logging):
113
+    mock_logger = mock.MagicMock()
114
+    mock_logging.return_value = mock_logger
115
+    mock_log_exception = mock.MagicMock()
116
+    mock_log_warning = mock.MagicMock()
117
+    mock_log_info = mock.MagicMock()
118
+    mock_logger.exception = mock_log_exception
119
+    mock_logger.warning = mock_log_warning
120
+    mock_logger.info = mock_log_info
121
+    release = 'master'
122
+    hash_name = 'current-tripleo'
123
+    repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
124
+                (release, hash_name))
125
+    mock_get_exception = Exception("We need more power!")
126
+    mock_get.side_effect = mock_get_exception
127
+    with pytest.raises(RuntimeError):
128
+        get_dlrn_hash(release, hash_name, retries=10)
129
+    mock_get.assert_called_with(repo_url, timeout=4)
130
+    mock_log_info.assert_not_called()
131
+    mock_log_warning.assert_called_with("Attempt 10 of 10 to get DLRN hash "
132
+                                        "threw an exception.")
133
+    assert mock_log_warning.call_count == 10
134
+    mock_log_exception.assert_called_with(mock_get_exception)
135
+    assert mock_log_exception.call_count == 10
136
+
137
+
138
+@mock.patch('logging.getLogger')
139
+@mock.patch('requests.get')
140
+def test_get_dlrn_hash_500_10_times(mock_get, mock_logging):
141
+    mock_logger = mock.MagicMock()
142
+    mock_logging.return_value = mock_logger
143
+    mock_log_exception = mock.MagicMock()
144
+    mock_log_warning = mock.MagicMock()
145
+    mock_log_info = mock.MagicMock()
146
+    mock_logger.exception = mock_log_exception
147
+    mock_logger.warning = mock_log_warning
148
+    mock_logger.info = mock_log_info
149
+    mock_response = mock.Mock()
150
+    mock_response.ok = False
151
+    mock_response.status_code = 500
152
+    release = 'master'
153
+    hash_name = 'current-tripleo'
154
+    repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
155
+                (release, hash_name))
156
+    mock_get.return_value = mock_response
157
+    with pytest.raises(RuntimeError):
158
+        get_dlrn_hash(release, hash_name, retries=10)
159
+    mock_get.assert_called_with(repo_url, timeout=4)
160
+    mock_log_info.assert_not_called()
161
+    mock_log_warning.assert_called_with("Attempt 10 of 10 to get DLRN hash "
162
+                                        "returned status code 500.")
163
+    assert mock_log_warning.call_count == 10
164
+    mock_log_exception.assert_not_called()

+ 45
- 10
scripts/emit_releases_file/test_release_name.py View File

@@ -1,8 +1,10 @@
1 1
 from emit_releases_file import compose_releases_dictionary
2 2
 
3
+import mock
3 4
 import pytest
4 5
 
5 6
 
7
+@mock.patch('emit_releases_file.get_dlrn_hash')
6 8
 @pytest.mark.parametrize('stable_release,expected_releases',
7 9
                          [('master', {
8 10
                              'undercloud_install_release': 'master',
@@ -10,7 +12,7 @@ import pytest
10 12
                              'undercloud_target_release': 'master',
11 13
                              'undercloud_target_hash': 'current-tripleo',
12 14
                              'overcloud_deploy_release': 'queens',
13
-                             'overcloud_deploy_hash': 'current-tripleo',
15
+                             'overcloud_deploy_hash': 'old-current-tripleo',
14 16
                              'overcloud_target_release': 'master',
15 17
                              'overcloud_target_hash': 'current-tripleo',
16 18
                          }), ('queens', {
@@ -19,7 +21,7 @@ import pytest
19 21
                              'undercloud_target_release': 'queens',
20 22
                              'undercloud_target_hash': 'current-tripleo',
21 23
                              'overcloud_deploy_release': 'pike',
22
-                             'overcloud_deploy_hash': 'current-tripleo',
24
+                             'overcloud_deploy_hash': 'old-current-tripleo',
23 25
                              'overcloud_target_release': 'queens',
24 26
                              'overcloud_target_hash': 'current-tripleo',
25 27
                          }), ('pike', {
@@ -28,20 +30,27 @@ import pytest
28 30
                              'undercloud_target_release': 'pike',
29 31
                              'undercloud_target_hash': 'current-tripleo',
30 32
                              'overcloud_deploy_release': 'ocata',
31
-                             'overcloud_deploy_hash': 'current-tripleo',
33
+                             'overcloud_deploy_hash': 'old-current-tripleo',
32 34
                              'overcloud_target_release': 'pike',
33 35
                              'overcloud_target_hash': 'current-tripleo',
34 36
                          })])
35
-def test_overcloud_upgrade_is_n_minus_one_to_n(stable_release,
37
+def test_overcloud_upgrade_is_n_minus_one_to_n(mock_get_hash,
38
+                                               stable_release,
36 39
                                                expected_releases):
40
+    mock_get_hash.side_effect = ['current-tripleo', 'old-current-tripleo']
37 41
     featureset = {
38 42
         'mixed_upgrade': True,
39 43
         'overcloud_upgrade': True,
40 44
     }
41 45
     assert (compose_releases_dictionary(stable_release,
42 46
                                         featureset) == expected_releases)
47
+    mock_get_hash.assert_has_calls(
48
+            [mock.call(stable_release, 'current-tripleo'),
49
+             mock.call(expected_releases['overcloud_deploy_release'],
50
+             'current-tripleo')])
43 51
 
44 52
 
53
+@mock.patch('emit_releases_file.get_dlrn_hash')
45 54
 @pytest.mark.parametrize('stable_release,expected_releases', [
46 55
     ('queens', {
47 56
         'undercloud_install_release': 'queens',
@@ -49,25 +58,32 @@ def test_overcloud_upgrade_is_n_minus_one_to_n(stable_release,
49 58
         'undercloud_target_release': 'queens',
50 59
         'undercloud_target_hash': 'current-tripleo',
51 60
         'overcloud_deploy_release': 'newton',
52
-        'overcloud_deploy_hash': 'current-tripleo',
61
+        'overcloud_deploy_hash': 'old-current-tripleo',
53 62
         'overcloud_target_release': 'queens',
54 63
         'overcloud_target_hash': 'current-tripleo',
55 64
     }),
56 65
 ])
57
-def test_ffu_overcloud_upgrade_is_n_minus_three_to_n(stable_release,
66
+def test_ffu_overcloud_upgrade_is_n_minus_three_to_n(mock_get_hash,
67
+                                                     stable_release,
58 68
                                                      expected_releases):
69
+    mock_get_hash.side_effect = ['current-tripleo', 'old-current-tripleo']
59 70
     featureset = {
60 71
         'mixed_upgrade': True,
61 72
         'ffu_overcloud_upgrade': True,
62 73
     }
63 74
     assert (compose_releases_dictionary(stable_release,
64 75
                                         featureset) == expected_releases)
76
+    mock_get_hash.assert_has_calls(
77
+            [mock.call(stable_release, 'current-tripleo'),
78
+             mock.call(expected_releases['overcloud_deploy_release'],
79
+             'current-passed-ci')])
65 80
 
66 81
 
82
+@mock.patch('emit_releases_file.get_dlrn_hash')
67 83
 @pytest.mark.parametrize('stable_release,expected_releases', [
68 84
     ('master', {
69 85
         'undercloud_install_release': 'queens',
70
-        'undercloud_install_hash': 'current-tripleo',
86
+        'undercloud_install_hash': 'old-current-tripleo',
71 87
         'undercloud_target_release': 'master',
72 88
         'undercloud_target_hash': 'current-tripleo',
73 89
         'overcloud_deploy_release': 'master',
@@ -76,15 +92,22 @@ def test_ffu_overcloud_upgrade_is_n_minus_three_to_n(stable_release,
76 92
         'overcloud_target_hash': 'current-tripleo',
77 93
     }),
78 94
 ])
79
-def test_undercloud_upgrade_is_n_minus_one_to_n(stable_release,
95
+def test_undercloud_upgrade_is_n_minus_one_to_n(mock_get_hash,
96
+                                                stable_release,
80 97
                                                 expected_releases):
98
+    mock_get_hash.side_effect = ['current-tripleo', 'old-current-tripleo']
81 99
     featureset = {
82 100
         'undercloud_upgrade': True,
83 101
     }
84 102
     assert (compose_releases_dictionary(stable_release,
85 103
                                         featureset) == expected_releases)
104
+    mock_get_hash.assert_has_calls(
105
+            [mock.call(stable_release, 'current-tripleo'),
106
+             mock.call(expected_releases['undercloud_install_release'],
107
+             'current-tripleo')])
86 108
 
87 109
 
110
+@mock.patch('emit_releases_file.get_dlrn_hash')
88 111
 @pytest.mark.parametrize(
89 112
     'stable_release,expected_releases',
90 113
     [('master', {
@@ -106,14 +129,22 @@ def test_undercloud_upgrade_is_n_minus_one_to_n(stable_release,
106 129
         'overcloud_target_release': 'queens',
107 130
         'overcloud_target_hash': 'current-tripleo',
108 131
     })])
109
-def test_overcloud_update_target_is_hash(stable_release, expected_releases):
132
+def test_overcloud_update_target_is_hash(mock_get_hash,
133
+                                         stable_release,
134
+                                         expected_releases):
135
+    mock_get_hash.side_effect = ['current-tripleo', 'previous-current-tripleo']
110 136
     featureset = {
111 137
         'overcloud_update': True,
112 138
     }
113 139
     assert (compose_releases_dictionary(stable_release,
114 140
                                         featureset) == expected_releases)
141
+    mock_get_hash.assert_has_calls(
142
+            [mock.call(stable_release, 'current-tripleo'),
143
+             mock.call(expected_releases['overcloud_deploy_release'],
144
+             'previous-current-tripleo')])
115 145
 
116 146
 
147
+@mock.patch('emit_releases_file.get_dlrn_hash')
117 148
 @pytest.mark.parametrize('stable_release,expected_releases',
118 149
                          [('master', {
119 150
                              'undercloud_install_release': 'master',
@@ -152,7 +183,11 @@ def test_overcloud_update_target_is_hash(stable_release, expected_releases):
152 183
                              'overcloud_target_release': 'ocata',
153 184
                              'overcloud_target_hash': 'current-tripleo',
154 185
                          })])
155
-def test_noop_target_is_the_same(stable_release, expected_releases):
186
+def test_noop_target_is_the_same(mock_get_hash,
187
+                                 stable_release,
188
+                                 expected_releases):
189
+    mock_get_hash.return_value = 'current-tripleo'
156 190
     featureset = {}
157 191
     assert (compose_releases_dictionary(stable_release,
158 192
                                         featureset) == expected_releases)
193
+    mock_get_hash.assert_called_once_with(stable_release, 'current-tripleo')

Loading…
Cancel
Save