def test_poll_deployment_timeout(self): core_v1_mock = MagicMock() ext_v1_mock = MagicMock() pod_mock = MagicMock() status_mock = MagicMock() status_mock.restart_count = 0 condition_mock = MagicMock() condition_mock.type = 'Ready' condition_mock.status = 'True' pod_mock.status.conditions = [ condition_mock, ] states_mock = MagicMock() states_mock.state.waiting = None pod_mock.status.container_statuses = [ states_mock, ] with patch('apsconnectcli.cluster.sleep'), \ patch('apsconnectcli.cluster.sys'), \ patch('apsconnectcli.cluster.datetime') as datetime_mock, \ patch('apsconnectcli.cluster.timedelta') as timedelta_mock: datetime_mock.now.side_effect = [0, 100] timedelta_mock.return_value = 0 ext_v1_mock.read_namespaced_deployment.return_value.status.available_replicas = False core_v1_mock.list_namespaced_pod.return_value.items = [pod_mock] result = poll_deployment(core_v1_mock, ext_v1_mock, 'namespace', 'name') self.assertFalse(result.available) self.assertTrue('Timed out' in result.message)
def test_poll_deployment_waiting_unexpected(self): core_v1_mock = MagicMock() ext_v1_mock = MagicMock() pod_mock = MagicMock() status_mock = MagicMock() status_mock.restart_count = 0 condition_mock = MagicMock() condition_mock.type = 'Ready' condition_mock.status = 'True' pod_mock.status.conditions = [ condition_mock, ] states_mock = MagicMock() states_mock.state.waiting.reason = 'SomeWeirdError' states_mock.state.waiting.message = 'SomeErrorMessage' pod_mock.status.container_statuses = [ states_mock, ] with patch('apsconnectcli.cluster.get_log') as log_mock: log_mock.return_value = 'LOG' ext_v1_mock.read_namespaced_deployment.return_value.status.available_replicas = False core_v1_mock.list_namespaced_pod.return_value.items = [pod_mock] result = poll_deployment(core_v1_mock, ext_v1_mock, 'namespace', 'name') self.assertFalse(result.available) self.assertTrue('Unexpected error' in result.message) self.assertTrue('SomeWeirdError' in result.message) self.assertTrue('SomeErrorMessage' in result.message)
def test_poll_deployment_too_many_restarts(self): core_v1_mock = MagicMock() ext_v1_mock = MagicMock() pod_mock = MagicMock() status_mock = MagicMock() status_mock.restart_count = 10 condition_mock = MagicMock() condition_mock.type = 'Ready' condition_mock.status = 'False' condition_mock.reason = 'ContainersNotReady' pod_mock.status.conditions = [ condition_mock, ] pod_mock.status.container_statuses = [ status_mock, ] with patch('apsconnectcli.cluster.get_log') as log_mock: log_mock.return_value = 'LOG' ext_v1_mock.read_namespaced_deployment.return_value.status.available_replicas = False core_v1_mock.list_namespaced_pod.return_value.items = [pod_mock] result = poll_deployment(core_v1_mock, ext_v1_mock, 'namespace', 'name') self.assertFalse(result.available) self.assertTrue('Readiness check failed' in result.message) self.assertTrue('LOG' in result.message)
def test_poll_deployment_ok(self): core_v1_mock = MagicMock() ext_v1_mock = MagicMock() result = poll_deployment(core_v1_mock, ext_v1_mock, 'namespace', 'name') self.assertTrue(result.available) self.assertEqual(result.message, 'Deployment has just become available')
def test_poll_deployment_no_containers(self): core_v1_mock = MagicMock() ext_v1_mock = MagicMock() with patch( 'apsconnectcli.cluster.check_containers_exist') as exist_mock: exist_mock.return_value = False ext_v1_mock.read_namespaced_deployment.return_value.status.available_replicas = False result = poll_deployment(core_v1_mock, ext_v1_mock, 'namespace', 'name') self.assertFalse(result.available) self.assertTrue('No containers available' in result.message)
def _create_deployment(name, image, api, image_pull_secret=None, healthcheck_path='/', replicas=2, namespace='default', force=False, core_api=None): template = { 'apiVersion': 'extensions/v1beta1', 'kind': 'Deployment', 'metadata': { 'name': name, }, 'spec': { 'replicas': replicas, 'template': { 'metadata': { 'labels': { 'name': name, }, }, 'spec': { 'containers': [ { 'name': name, 'image': image, 'env': [ { 'name': 'CONFIG_FILE', 'value': '/config/config.yml', }, ], 'livenessProbe': { 'httpGet': { 'path': healthcheck_path, 'port': 80, }, }, 'readinessProbe': { 'httpGet': { 'path': healthcheck_path, 'port': 80, }, }, 'ports': [ { 'containerPort': 80, 'name': 'http-server', }, ], 'resources': { 'requests': { 'cpu': '100m', 'memory': '128Mi', }, # TODO need more limits by default? 'limits': { 'cpu': '200m', 'memory': '256Mi', }, }, 'volumeMounts': [ { 'mountPath': '/config', 'name': 'config-volume', }, ], }, ], 'volumes': [ { 'name': 'config-volume', 'secret': { 'secretName': name, }, }, ], }, }, }, } if image_pull_secret: image_pull_secret_tag = [{'name': image_pull_secret}] template['spec']['template']['spec'][ 'imagePullSecrets'] = image_pull_secret_tag if force: _delete_deployment(name, api=api, namespace=namespace, core_api=core_api) api.create_namespaced_deployment(namespace=namespace, body=template) # Check deployment availability sys.stdout.write("Waiting for deployment to become ready...") sys.stdout.flush() poll_result = poll_deployment(core_v1=core_api, ext_v1=api, namespace=namespace, name=name) print() if not poll_result.available: print(poll_result.message) sys.exit(1)