blob: 65e76ef93d28373c858aea761e311c683a6b1a99 [file] [log] [blame]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import textwrap
from copy import deepcopy
import pytest
from mock import Mock, call
from pystachio import Empty
from apache.aurora.client.cli import Context
from apache.aurora.client.cli.diff_formatter import DiffFormatter
from apache.aurora.client.cli.jobs import DiffCommand
from apache.aurora.client.cli.options import TaskInstanceKey
from apache.aurora.config import AuroraConfig
from apache.aurora.config.schema.base import Job
from apache.thermos.config.schema_base import MB, Process, Resources, Task
from .util import AuroraClientCommandTest, FakeAuroraCommandContext, mock_verb_options
from gen.apache.aurora.api.constants import ACTIVE_STATES
from gen.apache.aurora.api.ttypes import (
ConfigGroup,
Constraint,
GetJobUpdateDiffResult,
Range,
Result,
TaskQuery
)
class TestDiffFormatter(AuroraClientCommandTest):
def setUp(self):
self._fake_context = FakeAuroraCommandContext()
self._mock_options = mock_verb_options(DiffCommand())
self._mock_options.instance_spec = TaskInstanceKey(self.TEST_JOBKEY, [0, 1])
self._fake_context.set_options(self._mock_options)
self._mock_api = self._fake_context.get_api("west")
@classmethod
def get_job_config(self, is_cron=False):
return AuroraConfig(job=Job(
cluster='west',
role='bozo',
environment='test',
name='the_job',
service=True if not is_cron else False,
cron_schedule='* * * * *' if is_cron else Empty,
task=Task(
name='task',
processes=[Process(cmdline='ls -la', name='process')],
resources=Resources(cpu=1.0, ram=1024 * MB, disk=1024 * MB)
),
instances=3,
))
@classmethod
def get_job_update_diff_result(cls, task=None):
diff = cls.create_simple_success_response()
if task is None:
task = cls.create_task_config('foo')
diff.result = Result(getJobUpdateDiffResult=GetJobUpdateDiffResult(
add=set([ConfigGroup(
config=task,
instances=frozenset([Range(first=10, last=10), Range(first=12, last=14)]))]),
remove=frozenset(),
update=frozenset([ConfigGroup(
config=task,
instances=frozenset([Range(first=11, last=11)]))]),
unchanged=frozenset([ConfigGroup(
config=task,
instances=frozenset([Range(first=0, last=9)]))])
))
return diff
@classmethod
def get_job_update_no_change_diff_result(cls):
diff = cls.create_simple_success_response()
task = cls.create_task_config('foo')
diff.result = Result(getJobUpdateDiffResult=GetJobUpdateDiffResult(
add=frozenset(),
remove=frozenset(),
update=frozenset(),
unchanged=frozenset([ConfigGroup(
config=task,
instances=frozenset([Range(first=0, last=3)]))])
))
return diff
def test_show_job_update_diff_with_task_diff(self):
config = self.get_job_config()
self._fake_context.get_job_config = Mock(return_value=config)
formatter = DiffFormatter(self._fake_context, config)
local_task = self.create_scheduled_tasks()[0].assignedTask.task
local_task.constraints = set([Constraint(name='host'), Constraint(name='rack')])
self._mock_api.get_job_update_diff.return_value = self.get_job_update_diff_result()
formatter.show_job_update_diff(self._mock_options.instance_spec.instance, local_task)
assert self._mock_api.get_job_update_diff.mock_calls == [
call(config, self._mock_options.instance_spec.instance)
]
assert "\n".join(self._fake_context.get_out()) == textwrap.dedent("""\
This job update will:
add instances: [10], [12-14]
update instances: [11]
with diff:\n
2c2,3
< 'constraints': None,
---
> 'constraints': [ Constraint(name='host', constraint=None),
> Constraint(name='rack', constraint=None)],
\n
not change instances: [0-9]""")
def test_show_job_update_diff_no_diff_out_of_order_constraints(self):
config = self.get_job_config()
self._fake_context.get_job_config = Mock(return_value=config)
formatter = DiffFormatter(self._fake_context, config)
local_task = self.create_scheduled_tasks()[0].assignedTask.task
local_task.constraints = set([Constraint(name='host'), Constraint(name='rack')])
remote_task = deepcopy(local_task)
remote_task.constraints = set([Constraint(name='rack'), Constraint(name='host')])
self._mock_api.get_job_update_diff.return_value = self.get_job_update_diff_result(remote_task)
formatter.show_job_update_diff(self._mock_options.instance_spec.instance, local_task)
assert self._mock_api.get_job_update_diff.mock_calls == [
call(config, self._mock_options.instance_spec.instance)
]
assert "\n".join(self._fake_context.get_out()) == textwrap.dedent("""\
This job update will:
add instances: [10], [12-14]
update instances: [11]
with diff:\n\n\n
not change instances: [0-9]""")
def test_show_job_update_diff_without_task_diff(self):
config = self.get_job_config()
self._fake_context.get_job_config = Mock(return_value=config)
formatter = DiffFormatter(self._fake_context, config)
self._mock_api.get_job_update_diff.return_value = self.get_job_update_diff_result()
formatter.show_job_update_diff(self._mock_options.instance_spec.instance)
assert self._mock_api.get_job_update_diff.mock_calls == [
call(config, self._mock_options.instance_spec.instance)
]
assert "\n".join(self._fake_context.get_out()) == textwrap.dedent("""\
This job update will:
add instances: [10], [12-14]
update instances: [11]
not change instances: [0-9]""")
def test_show_job_update_diff_no_change(self):
config = self.get_job_config()
self._fake_context.get_job_config = Mock(return_value=config)
formatter = DiffFormatter(self._fake_context, config)
self._mock_api.get_job_update_diff.return_value = self.get_job_update_no_change_diff_result()
formatter.show_job_update_diff(self._mock_options.instance_spec.instance)
assert self._mock_api.get_job_update_diff.mock_calls == [
call(config, self._mock_options.instance_spec.instance)
]
assert "\n".join(self._fake_context.get_out()) == textwrap.dedent("""\
This job update will:
not change instances: [0-3]""")
def test_get_job_update_diff_error(self):
mock_config = self.get_job_config()
self._fake_context.get_job_config = Mock(return_value=mock_config)
formatter = DiffFormatter(self._fake_context, mock_config)
self._mock_api.get_job_update_diff.return_value = self.create_error_response()
with pytest.raises(Context.CommandError):
formatter.show_job_update_diff(self._mock_options.instance_spec.instance)
assert self._mock_api.get_job_update_diff.mock_calls == [
call(mock_config, self._mock_options.instance_spec.instance)
]
assert self._fake_context.get_out() == []
assert self._fake_context.get_err() == ["Error getting diff info from scheduler", "\tWhoops"]
def test_diff_no_update_details_success(self):
config = self.get_job_config(is_cron=True)
self._fake_context.get_job_config = Mock(return_value=config)
formatter = DiffFormatter(self._fake_context, config)
self._mock_api.query.return_value = self.create_empty_task_result()
query = TaskQuery(
jobKeys=[self.TEST_JOBKEY.to_thrift()],
statuses=ACTIVE_STATES)
self._mock_api.build_query.return_value = query
local_tasks = []
formatter.diff_no_update_details(local_tasks)