def test_raise_if_not_2xx(story, line): res = MagicMock() res.code = 401 with pytest.raises(K8sError): Kubernetes.raise_if_not_2xx(res) res.code = 200 assert Kubernetes.raise_if_not_2xx(res) is None
def test_is_2xx(): res = MagicMock() res.code = 200 assert Kubernetes.is_2xx(res) is True res.code = 210 assert Kubernetes.is_2xx(res) is True res.code = 300 assert Kubernetes.is_2xx(res) is False res.code = 400 assert Kubernetes.is_2xx(res) is False
def test_find_all_ports(): services = { 'alpine': { 'http': { 'port': 8080 } }, 'alpha': { 'expose': { 'console': { 'http': { 'port': 1882 } } }, 'http': { 'port': 9092, 'subscribe': { 'port': 9090 }, 'unsubscribe': { 'port': 9091 } } }, 'nested': { 'a': { 'b': { 'c': { 'd': { 'e': { 'http': { 'subscribe': { 'port': 1234 }, 'unsubscribe': { 'port': 1235 } } } } } } } } } assert Kubernetes.find_all_ports(services['alpine']) == {8080} assert Kubernetes.find_all_ports( services['alpha']) == {1882, 9090, 9091, 9092} assert Kubernetes.find_all_ports(services['nested']) == {1234, 1235}
async def test_does_resource_exist(patch, story, resource, async_mock, res_code): resp = MagicMock() resp.code = res_code patch.object(Kubernetes, 'make_k8s_call', new=async_mock(return_value=resp)) if res_code == 500 or resource == 'foo': with pytest.raises(Exception): await Kubernetes._does_resource_exist(story.app, resource, 'name') return ret = await Kubernetes._does_resource_exist(story.app, resource, 'name') if res_code == 200: assert ret is True else: assert ret is False expected_path = Kubernetes._get_api_path_prefix(resource) + \ f'/{story.app.app_id}/{resource}/name' Kubernetes.make_k8s_call.mock.assert_called_with(story.app.config, story.app.logger, expected_path)
async def test_delete_resource(patch, story, async_mock, first_res, resource): story.app.app_id = 'my_app' api_responses = [ _create_response(first_res), _create_response(200), _create_response(200), _create_response(404), ] patch.object(Kubernetes, 'make_k8s_call', new=async_mock(side_effect=api_responses)) patch.object(asyncio, 'sleep', new=async_mock()) if resource == 'unknown': with pytest.raises(Exception): await Kubernetes._delete_resource(story.app, resource, 'foo') return else: await Kubernetes._delete_resource(story.app, resource, 'foo') if first_res == 404: assert Kubernetes.make_k8s_call.mock.call_count == 1 return prefix = Kubernetes._get_api_path_prefix(resource) assert Kubernetes.make_k8s_call.mock.mock_calls == [ mock.call(story.app, f'{prefix}/my_app/{resource}/foo' f'?gracePeriodSeconds=0', method='delete'), mock.call(story.app, f'{prefix}/my_app/{resource}/foo'), mock.call(story.app, f'{prefix}/my_app/{resource}/foo'), mock.call(story.app, f'{prefix}/my_app/{resource}/foo'), ]
def test_get_liveness_probe(app, service): app.services = { service['name']: { 'configuration': service['configuration'] } } liveness_probe = Kubernetes.get_liveness_probe(app, service['name']) assert liveness_probe == service['liveness_probe']
async def test_check_for_image_errors(patch, app, async_mock): container_name = 'my_container' app.app_id = 'my_app' patch.object(Kubernetes, 'make_k8s_call', new=async_mock(side_effect=[ _create_response( 200, { 'items': [{ 'status': { 'containerStatuses': [{ 'image': 'test', 'state': { 'waiting': { 'reason': 'ContainerCreating' } } }] } }] }), _create_response( 200, { 'items': [{ 'status': { 'containerStatuses': [{ 'image': 'test', 'state': { 'waiting': { 'reason': 'ImagePullBackOff' } } }] } }] }), ])) await Kubernetes.check_for_image_errors(app, container_name) with pytest.raises(K8sError) as exc: await Kubernetes.check_for_image_errors(app, container_name) assert exc.value.message == 'ImagePullBackOff - Failed to pull image test' prefix = Kubernetes._get_api_path_prefix('pods') qs = urllib.parse.urlencode({'labelSelector': f'app={container_name}'}) Kubernetes.make_k8s_call.mock.assert_called() Kubernetes.make_k8s_call.mock.assert_called_with( app.config, app.logger, f'{prefix}/{app.app_id}' f'/pods?{qs}')
async def test_create_ingress(patch, app, async_mock, resource_exists, k8s_api_returned_2xx): if resource_exists and not k8s_api_returned_2xx: # Invalid combination, since if the ing resource exists already, # no additional call to the k8s API is made. return app.app_id = 'my_app_id' app.config.INGRESS_GLOBAL_STATIC_IP_NAME = 'ip-static-name-global' ingress_name = 'my_ingress_name' hostname = 'my_ingress_hostname' container_name = 'my_container_name' expose = Expose(service='service', service_expose_name='expose_name', http_path='expose_path') http_conf = {'path': '/my_app', 'port': 6000} app.services = { expose.service: { ServiceConstants.config: { KEY_EXPOSE: { expose.service_expose_name: { 'http': http_conf } } } } } app.config.APP_DOMAIN = 'foo.com' patch.object(Kubernetes, '_does_resource_exist', new=async_mock(return_value=resource_exists)) expected_payload = { 'apiVersion': 'extensions/v1beta1', 'kind': 'Ingress', 'metadata': { 'name': ingress_name, 'annotations': { 'kubernetes.io/ingress.class': 'nginx', 'kubernetes.io/ingress.global-static-ip-name': app.config.INGRESS_GLOBAL_STATIC_IP_NAME, 'ingress.kubernetes.io/rewrite-target': expose.http_path, 'nginx.ingress.kubernetes.io/proxy-body-size': '1m', 'nginx.ingress.kubernetes.io/proxy-read-timeout': '120' } }, 'spec': { 'tls': [{ 'hosts': [f'{hostname}.' f'{app.config.APP_DOMAIN}'] }], 'rules': [{ 'host': f'{hostname}.{app.config.APP_DOMAIN}', 'http': { 'paths': [{ 'path': http_conf['path'], 'backend': { 'serviceName': container_name, 'servicePort': http_conf['port'] } }] } }] } } patch.object(Kubernetes, 'make_k8s_call', new=async_mock(return_value=314)) patch.object(Kubernetes, 'is_2xx', return_value=k8s_api_returned_2xx) if k8s_api_returned_2xx: await Kubernetes.create_ingress(ingress_name, app, expose, container_name, hostname) else: with pytest.raises(K8sError): await Kubernetes.create_ingress(ingress_name, app, expose, container_name, hostname) return if resource_exists: Kubernetes.make_k8s_call.mock.assert_not_called() else: prefix = Kubernetes._get_api_path_prefix('ingresses') prefix = f'{prefix}/{app.app_id}/ingresses' Kubernetes.make_k8s_call.mock.assert_called_with( app.config, app.logger, prefix, payload=expected_payload) Kubernetes.is_2xx.assert_called_with(314)
def test_new_ssl_context(): assert isinstance(Kubernetes.new_ssl_context(), ssl.SSLContext)
def test_get_hostname(story): story.app.app_id = 'my_app' container_name = 'alpine' ret = Kubernetes.get_hostname(story.app, container_name) assert ret == 'alpine.my_app.svc.cluster.local'