Skip to content

Commit 7f6027b

Browse files
chansmith97kurtisvg
authored andcommitted
Adding compute-legacy-endpoint-access sample (GoogleCloudPlatform#2517)
1 parent bca7097 commit 7f6027b

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import json
2+
import requests
3+
import time
4+
5+
"""Example of using the Metadata Server to watch deprecated endpoint accesses.
6+
7+
For more information, see the README.md under /compute.
8+
"""
9+
10+
11+
METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1'
12+
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
13+
14+
15+
# [START compute_wait_for_legacy_usage]
16+
def wait_for_legacy_usage(callback):
17+
url = '{}/instance/legacy-endpoint-access'.format(METADATA_URL)
18+
last_etag = '0'
19+
counts = None
20+
while True:
21+
r = requests.get(
22+
url,
23+
params={
24+
'last_etag': last_etag,
25+
'recursive': True,
26+
'wait_for_change': True
27+
},
28+
headers=METADATA_HEADERS)
29+
if r.status_code == 503: # Metadata server unavailable
30+
print('Metadata server unavailable. Sleeping for 1 second.')
31+
time.sleep(1)
32+
continue
33+
if r.status_code == 404: # Feature not yet supported
34+
print('Legacy endpoint access not supported. Sleeping for 1 hour.')
35+
time.sleep(3600)
36+
continue
37+
r.raise_for_status()
38+
39+
last_etag = r.headers['etag']
40+
access_info = json.loads(r.text)
41+
if not counts:
42+
counts = access_info
43+
if access_info != counts:
44+
diff = {
45+
ver: access_info[ver] - counts[ver] for ver in counts
46+
}
47+
counts = access_info
48+
callback(diff)
49+
# [END compute_wait_for_legacy_usage]
50+
51+
52+
def legacy_callback(diff):
53+
print(
54+
'Since last message, {} new requests to 0.1 endpoints and {} new '
55+
'requests to v1beta1 endpoints.'.format(diff['0.1'], diff['v1beta1']))
56+
57+
58+
def main():
59+
wait_for_legacy_usage(legacy_callback)
60+
61+
62+
if __name__ == '__main__':
63+
main()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import mock
2+
import requests
3+
import json
4+
5+
import detect_legacy_usage
6+
7+
8+
def execute_test(requests_mock, *responses):
9+
callback_mock = mock.Mock()
10+
requests_mock.codes.ok = requests.codes.ok
11+
requests_mock.get.side_effect = responses + (StopIteration(),)
12+
try:
13+
detect_legacy_usage.wait_for_legacy_usage(callback_mock)
14+
except StopIteration:
15+
return callback_mock
16+
17+
18+
@mock.patch('detect_legacy_usage.requests')
19+
@mock.patch('detect_legacy_usage.time')
20+
def test_metadata_server_unavailable(time_mock, requests_mock):
21+
# Metadata server unavailable
22+
response_mock = mock.Mock()
23+
response_mock.status_code = 503
24+
25+
callback_mock = execute_test(requests_mock, response_mock)
26+
27+
assert callback_mock.call_count == 0
28+
assert time_mock.sleep.call_count == 1
29+
assert time_mock.sleep.call_args_list[0][0] == (1,)
30+
31+
32+
@mock.patch('detect_legacy_usage.requests')
33+
@mock.patch('detect_legacy_usage.time')
34+
def test_endpoint_does_not_exist(time_mock, requests_mock):
35+
# legacy-endpoint-access url unavailable (removed or not yet supported)
36+
response_mock = mock.Mock()
37+
response_mock.status_code = 404
38+
39+
callback_mock = execute_test(requests_mock, response_mock)
40+
41+
assert callback_mock.call_count == 0
42+
assert time_mock.sleep.call_count == 1
43+
assert time_mock.sleep.call_args_list[0][0] == (3600,)
44+
45+
46+
@mock.patch('detect_legacy_usage.requests')
47+
@mock.patch('detect_legacy_usage.time')
48+
def test_callback_called_on_change(time_mock, requests_mock):
49+
# Response 1 has starting counts (should not trigger callback)
50+
response1_data = {'0.1': 5, 'v1beta1': 10}
51+
response1_mock = mock.Mock()
52+
response1_mock.status_code = 200
53+
response1_mock.text = json.dumps(response1_data)
54+
response1_mock.headers = {'etag': '1'}
55+
56+
# Response 2 has different data
57+
response2_data = {'0.1': 6, 'v1beta1': 10}
58+
response2_mock = mock.Mock()
59+
response2_mock.status_code = 200
60+
response2_mock.text = json.dumps(response2_data)
61+
response2_mock.headers = {'etag': '2'}
62+
63+
callback_mock = execute_test(requests_mock, response1_mock, response2_mock)
64+
65+
# One change so callback is called once
66+
assert callback_mock.call_count == 1
67+
assert callback_mock.call_args_list[0][0] == ({'0.1': 1, 'v1beta1': 0},)
68+
assert time_mock.sleep.call_count == 0
69+
70+
71+
@mock.patch('detect_legacy_usage.requests')
72+
@mock.patch('detect_legacy_usage.time')
73+
def test_callback_not_called_without_change(time_mock, requests_mock):
74+
# Response 1 has starting counts (should not trigger callback)
75+
response1_data = {'0.1': 5, 'v1beta1': 10}
76+
response1_mock = mock.Mock()
77+
response1_mock.status_code = 200
78+
response1_mock.text = json.dumps(response1_data)
79+
response1_mock.headers = {'etag': '1'}
80+
81+
# Response 2 has the same data (no change)
82+
response2_mock = mock.Mock()
83+
response2_mock.status_code = 200
84+
response2_mock.text = json.dumps(response1_data)
85+
response2_mock.headers = {'etag': '1'}
86+
87+
callback_mock = execute_test(requests_mock, response1_mock, response2_mock)
88+
89+
# No change so callback is not called
90+
assert callback_mock.call_count == 0
91+
assert time_mock.sleep.call_count == 0

0 commit comments

Comments
 (0)