def test_validate_http_request(self): """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" wire_protocol_client = wire.WireProtocol(wireserver_url).client test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) status_blob = wire_protocol_client.status_blob status_blob.data = faux_status status_blob.vm_status = restapi.VMStatus(message="Ready", status="Ready") exp_method = 'PUT' exp_url = hostplugin_status_url exp_data = self._hostplugin_data( status_blob.get_block_blob_headers(len(faux_status)), bytearray(faux_status, encoding='utf-8')) with patch.object(restutil, "http_request") as patch_http: patch_http.return_value = Mock(status=httpclient.OK) wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state) plugin = wire_protocol_client.get_host_plugin() with patch.object(plugin, 'get_api_versions') as patch_api: patch_api.return_value = API_VERSION plugin.put_vm_status(status_blob, sas_url, block_blob_type) self.assertTrue(patch_http.call_count == 1) self._validate_hostplugin_args( patch_http.call_args_list[0], test_goal_state, exp_method, exp_url, exp_data)
def test_validate_get_extension_artifacts(self): test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) expected_url = hostplugin.URI_FORMAT_GET_EXTENSION_ARTIFACT.format( wireserver_url, hostplugin.HOST_PLUGIN_PORT) expected_headers = { 'x-ms-version': '2015-09-01', "x-ms-containerid": test_goal_state.container_id, "x-ms-host-config-name": test_goal_state.role_config_name, "x-ms-artifact-location": sas_url } host_client = wire.HostPluginProtocol(wireserver_url, test_goal_state.container_id, test_goal_state.role_config_name) self.assertFalse(host_client.is_initialized) self.assertTrue(host_client.api_versions is None) self.assertTrue(host_client.health_service is not None) with patch.object(wire.HostPluginProtocol, "get_api_versions", return_value=api_versions) as patch_get: actual_url, actual_headers = host_client.get_artifact_request( sas_url) self.assertTrue(host_client.is_initialized) self.assertFalse(host_client.api_versions is None) self.assertEqual(expected_url, actual_url) for k in expected_headers: self.assertTrue(k in actual_headers) self.assertEqual(expected_headers[k], actual_headers[k])
def test_validate_http_request(self): """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" from azurelinuxagent.common.protocol.hostplugin import API_VERSION from azurelinuxagent.common.utils import restutil exp_method = 'PUT' exp_url = 'http://{0}:32526/status'.format(wireserver_url) exp_data = '{"content": "eyJkdW1teSI6ICJkYXRhIn0=", "headers": [{"headerName": ' \ '"x-ms-version", "headerValue": "2014-02-14"}, ' \ '{"headerName": "x-ms-blob-type", "headerValue": null}], ' \ '"requestUri": "http://sas_url"}' test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) with patch.object(restutil, "http_request") as patch_http: wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) plugin = wire_protocol_client.get_host_plugin() blob = wire_protocol_client.status_blob blob.vm_status = restapi.VMStatus() blob.data = '{"dummy": "data"}' with patch.object(plugin, 'get_api_versions') as patch_api: patch_api.return_value = API_VERSION plugin.put_vm_status(blob, sas_url) self.assertTrue(patch_http.call_count == 1) self.assertTrue(patch_http.call_args[0][0] == exp_method) self.assertTrue(patch_http.call_args[0][1] == exp_url) self.assertTrue(patch_http.call_args[0][2] == exp_data) # Assert headers headers = patch_http.call_args[1]['headers'] self.assertEqual(headers['x-ms-containerid'], test_goal_state.container_id) self.assertEqual(headers['x-ms-host-config-name'], test_goal_state.role_instance_config_name)
def test_validate_block_blob(self): """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" wire_protocol_client = wire.WireProtocol(wireserver_url).client test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) host_client = wire.HostPluginProtocol(wireserver_url, test_goal_state.container_id, test_goal_state.role_config_name) self.assertFalse(host_client.is_initialized) self.assertTrue(host_client.api_versions is None) status_blob = wire_protocol_client.status_blob status_blob.data = faux_status status_blob.type = block_blob_type status_blob.vm_status = restapi.VMStatus(message="Ready", status="Ready") exp_method = 'PUT' exp_url = hostplugin_status_url exp_data = self._hostplugin_data( status_blob.get_block_blob_headers(len(faux_status)), bytearray(faux_status, encoding='utf-8')) with patch.object(restutil, "http_request") as patch_http: patch_http.return_value = Mock(status=httpclient.OK) with patch.object(wire.HostPluginProtocol, "get_api_versions") as patch_get: patch_get.return_value = api_versions host_client.put_vm_status(status_blob, sas_url) self.assertTrue(patch_http.call_count == 1) self._validate_hostplugin_args( patch_http.call_args_list[0], test_goal_state, exp_method, exp_url, exp_data)
def test_fallback_failure(self): """ Validate that when host plugin fails, the default channel is reset """ test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire.HostPluginProtocol.set_default_channel(False) with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True): with patch.object(wire.StatusBlob, "upload", return_value=False): with patch.object(wire.HostPluginProtocol, "_put_page_blob_status", side_effect=wire.HttpError("put failure")) as patch_put: client = wire.WireProtocol(wireserver_url).client client.get_goal_state = Mock(return_value=test_goal_state) client.ext_conf = wire.ExtensionsConfig(None) client.ext_conf.status_upload_blob = sas_url client.ext_conf.status_upload_blob_type = page_blob_type client.status_blob.set_vm_status(status) client.upload_status_blob() self.assertTrue(patch_put.call_count == 1, "Fallback was not engaged") self.assertFalse(wire.HostPluginProtocol.is_default_channel())
def test_put_status_error_reporting(self): """ Validate the telemetry when uploading status fails """ test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire.HostPluginProtocol.set_default_channel(False) with patch.object(wire.StatusBlob, "upload", return_value=False): wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.status_blob.set_vm_status(status) put_error = wire.HttpError("put status http error") with patch.object(event, "add_event") as patch_add_event: with patch.object(restutil, "http_put", side_effect=put_error) as patch_http_put: with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True): wire_protocol_client.upload_status_blob() self.assertFalse(wire.HostPluginProtocol.is_default_channel()) self.assertTrue(patch_add_event.call_count == 1)
def test_fallback(self): """ Validate fallback to upload status using HostGAPlugin is happening when status reporting via default method is unsuccessful """ test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True): with patch.object(wire.StatusBlob, "upload", return_value=False) as patch_upload: with patch.object(wire.HostPluginProtocol, "_put_page_blob_status") as patch_put: wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type wire_protocol_client.status_blob.set_vm_status(status) wire_protocol_client.upload_status_blob() self.assertEqual(patch_upload.call_count, 1) self.assertTrue(patch_put.call_count == 1, "Fallback was not engaged") self.assertTrue(patch_put.call_args[0][0] == sas_url) self.assertTrue(wire.HostPluginProtocol.is_default_channel()) wire.HostPluginProtocol.set_default_channel(False)
def test_put_status_error_reporting(self, patch_add_event): """ Validate the telemetry when uploading status fails """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire.HostPluginProtocol.set_default_channel(False) with patch.object(wire.StatusBlob, "upload", return_value=False): wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.status_blob.set_vm_status(status) put_error = wire.HttpError("put status http error") with patch.object(restutil, "http_put", side_effect=put_error) as patch_http_put: with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True): wire_protocol_client.upload_status_blob() # The agent tries to upload via HostPlugin and that fails due to # http_put having a side effect of "put_error" # # The agent tries to upload using a direct connection, and that succeeds. self.assertEqual( 1, wire_protocol_client.status_blob.upload.call_count) # The agent never touches the default protocol is this code path, so no change. self.assertFalse( wire.HostPluginProtocol.is_default_channel()) # The agent never logs a telemetry event for a bad HTTP call self.assertEqual(patch_add_event.call_count, 0)
def test_fallback(self): """ Status now defaults to HostPlugin. Validate that any errors on the public channel are ignored. Validate that the default channel is never changed as part of status upload. """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True): with patch.object(wire.StatusBlob, "upload", return_value=False) as patch_upload: with patch.object(wire.HostPluginProtocol, "_put_page_blob_status") as patch_put: wire_protocol_client = wire.WireProtocol( wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type wire_protocol_client.status_blob.set_vm_status(status) wire_protocol_client.upload_status_blob() self.assertEqual(patch_upload.call_count, 0) self.assertTrue(patch_put.call_count == 1, "Fallback was not engaged") self.assertTrue(patch_put.call_args[0][0] == sas_url) self.assertFalse( wire.HostPluginProtocol.is_default_channel())
def _init_host(self): test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) host_plugin = wire.HostPluginProtocol(wireserver_url, test_goal_state.container_id, test_goal_state.role_config_name) self.assertTrue(host_plugin.health_service is not None) return host_plugin
def test_validate_page_blobs(self): """Validate correct set of data is sent for page blobs""" wire_protocol_client = wire.WireProtocol(wireserver_url).client test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) host_client = wire.HostPluginProtocol(wireserver_url, test_goal_state.container_id, test_goal_state.role_config_name) self.assertFalse(host_client.is_initialized) self.assertTrue(host_client.api_versions is None) status_blob = wire_protocol_client.status_blob status_blob.data = faux_status status_blob.type = page_blob_type status_blob.vm_status = restapi.VMStatus(message="Ready", status="Ready") exp_method = 'PUT' exp_url = hostplugin_status_url page_status = bytearray(status_blob.data, encoding='utf-8') page_size = int((len(page_status) + 511) / 512) * 512 page_status = bytearray(status_blob.data.ljust(page_size), encoding='utf-8') page = bytearray(page_size) page[0:page_size] = page_status[0:len(page_status)] mock_response = MockResponse('', httpclient.OK) with patch.object(restutil, "http_request", return_value=mock_response) as patch_http: with patch.object(wire.HostPluginProtocol, "get_api_versions") as patch_get: patch_get.return_value = api_versions host_client.put_vm_status(status_blob, sas_url) self.assertTrue(patch_http.call_count == 3) # first call is to host plugin exp_data = self._hostplugin_data( status_blob.get_page_blob_create_headers(page_size)) self._validate_hostplugin_args(patch_http.call_args_list[0], test_goal_state, exp_method, exp_url, exp_data) # second call is to health service self.assertEqual('POST', patch_http.call_args_list[1][0][0]) self.assertEqual(health_service_url, patch_http.call_args_list[1][0][1]) # last call is to host plugin exp_data = self._hostplugin_data( status_blob.get_page_blob_page_headers(0, page_size), page) exp_data['requestUri'] += "?comp=page" self._validate_hostplugin_args(patch_http.call_args_list[2], test_goal_state, exp_method, exp_url, exp_data)
def test_validate_http_put(self): """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) expected_url = "http://168.63.129.16:32526/status" expected_headers = { 'x-ms-version': '2015-09-01', "Content-type": "application/json", "x-ms-containerid": test_goal_state.container_id, "x-ms-host-config-name": test_goal_state.role_config_name } expected_content = '{"content": "eyJkdW1teSI6ICJkYXRhIn0=", ' \ '"headers": [{"headerName": "x-ms-version", ' \ '"headerValue": "2014-02-14"}, ' \ '{"headerName": "x-ms-blob-type", "headerValue": ' \ '"BlockBlob"}], ' \ '"requestUri": "http://sas_url"}' host_client = wire.HostPluginProtocol(wireserver_url, test_goal_state.container_id, test_goal_state.role_config_name) self.assertFalse(host_client.is_initialized) self.assertTrue(host_client.api_versions is None) status_blob = wire.StatusBlob(None) status_blob.vm_status = restapi.VMStatus(message="Ready", status="Ready") status_blob.data = '{"dummy": "data"}' status_blob.type = "BlockBlob" with patch.object(wire.HostPluginProtocol, "get_api_versions") as patch_get: patch_get.return_value = api_versions with patch.object(restapi.restutil, "http_put") as patch_put: patch_put.return_value = MagicMock() host_client.put_vm_status(status_blob, sas_url) self.assertTrue(host_client.is_initialized) self.assertFalse(host_client.api_versions is None) self.assertTrue(patch_put.call_count == 1) self.assertTrue(patch_put.call_args[0][0] == expected_url) self.assertTrue( patch_put.call_args[1]['data'] == expected_content) self.assertTrue( patch_put.call_args[1]['headers'] == expected_headers)
def test_fallback(self): """ Validate fallback to upload status using HostGAPlugin is happening when status reporting via default method is unsuccessful """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) with patch.object(wire.HostPluginProtocol, "put_vm_status") as patch_put: with patch.object(wire.StatusBlob, "upload", return_value=False) as patch_upload: wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.upload_status_blob() self.assertTrue(patch_put.call_count == 1, "Fallback was not engaged") self.assertTrue(patch_put.call_args[0][1] == sas_url)
def test_fallback_channel_failure(self, patch_update, patch_put, patch_upload, _): """ When host plugin returns a 500, and direct fails, we should raise a ProtocolError """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type wire_protocol_client.status_blob.set_vm_status(status) # act self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob) # assert direct route is not called self.assertEqual(1, patch_upload.call_count, "Direct channel was not used") # assert host plugin route is called self.assertEqual(1, patch_put.call_count, "Host plugin was not used") # assert update goal state is called twice, forced=True on the second self.assertEqual(1, patch_update.call_count, "Update goal state unexpected call count") self.assertEqual(0, len(patch_update.call_args[1]), "Update goal state unexpected call count") # ensure the correct url is used self.assertEqual(sas_url, patch_put.call_args[0][0]) # ensure host plugin is not set as default self.assertFalse(wire.HostPluginProtocol.is_default_channel())
def test_fallback_channel_410(self, patch_refresh_host_plugin, patch_update, patch_put, patch_upload, _): """ When host plugin returns a 410, we should force the goal state update and return """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type wire_protocol_client.status_blob.set_vm_status(status) # act wire_protocol_client.upload_status_blob() # assert direct route is not called self.assertEqual(0, patch_upload.call_count, "Direct channel was used") # assert host plugin route is called self.assertEqual(1, patch_put.call_count, "Host plugin was not used") # assert update goal state is called with no arguments (forced=False), then update_host_plugin_from_goal_state is called self.assertEqual(1, patch_update.call_count, "Update goal state unexpected call count") self.assertEqual(0, len(patch_update.call_args[1]), "Update goal state unexpected argument count") self.assertEqual(1, patch_refresh_host_plugin.call_count, "Refresh host plugin unexpected call count") # ensure the correct url is used self.assertEqual(sas_url, patch_put.call_args[0][0]) # ensure host plugin is not set as default self.assertFalse(wire.HostPluginProtocol.is_default_channel())
def test_default_channel(self, patch_update, patch_put, patch_upload, _): """ Status now defaults to HostPlugin. Validate that any errors on the public channel are ignored. Validate that the default channel is never changed as part of status upload. """ test_goal_state = wire.GoalState( WireProtocolData(DATA_FILE).goal_state) status = restapi.VMStatus(status="Ready", message="Guest Agent is running") wire_protocol_client = wire.WireProtocol(wireserver_url).client wire_protocol_client.get_goal_state = Mock( return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type wire_protocol_client.status_blob.set_vm_status(status) # act wire_protocol_client.upload_status_blob() # assert direct route is not called self.assertEqual(0, patch_upload.call_count, "Direct channel was used") # assert host plugin route is called self.assertEqual(1, patch_put.call_count, "Host plugin was not used") # assert update goal state is only called once, non-forced self.assertEqual(1, patch_update.call_count, "Unexpected call count") self.assertEqual(0, len(patch_update.call_args[1]), "Unexpected parameters") # ensure the correct url is used self.assertEqual(sas_url, patch_put.call_args[0][0]) # ensure host plugin is not set as default self.assertFalse(wire.HostPluginProtocol.is_default_channel())