def setUp(self):
   self._command = StartUpdate()
   self._job_key = AuroraJobKey("cluster", "role", "env", "job")
   self._mock_options = mock_verb_options(self._command)
   self._mock_options.instance_spec = TaskInstanceKey(self._job_key, [])
   self._fake_context = FakeAuroraCommandContext()
   self._fake_context.set_options(self._mock_options)
   self._mock_api = self._fake_context.get_api('UNUSED')
Exemple #2
0
 def setUp(self):
     self._command = StartUpdate()
     self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
     self._mock_options = mock_verb_options(self._command)
     self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
     self._fake_context = FakeAuroraCommandContext()
     self._fake_context.set_options(self._mock_options)
     self._mock_api = self._fake_context.get_api('UNUSED')
 def setUp(self):
   self._command = StartUpdate()
   self._job_key = AuroraJobKey("cluster", "role", "env", "job")
   self._mock_options = mock_verb_options(self._command)
   self._mock_options.instance_spec = TaskInstanceKey(self._job_key, [])
   self._fake_context = FakeAuroraCommandContext()
   self._fake_context.set_options(self._mock_options)
   self._mock_api = self._fake_context.get_api("test")
Exemple #4
0
 def setUp(self):
   self._command = StartUpdate()
   self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
   self._mock_options = mock_verb_options(self._command)
   self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
   self._fake_context = FakeAuroraCommandContext()
   self._fake_context.set_options(self._mock_options)
   self._mock_api = self._fake_context.get_api('UNUSED')
class TestStartUpdateCommand(AuroraClientCommandTest):

  def setUp(self):
    self._command = StartUpdate()
    self._job_key = AuroraJobKey("cluster", "role", "env", "job")
    self._mock_options = mock_verb_options(self._command)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, [])
    self._fake_context = FakeAuroraCommandContext()
    self._fake_context.set_options(self._mock_options)
    self._mock_api = self._fake_context.get_api("test")

  @classmethod
  def create_mock_config(cls, is_cron=False):
    mock_config = create_autospec(spec=AuroraConfig, spec_set=True, instance=True)
    mock_raw_config = Mock()
    mock_raw_config.has_cron_schedule.return_value = is_cron
    mock_config.raw = Mock(return_value=mock_raw_config)
    return mock_config

  def test_start_update_with_lock(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
        ResponseCode.LOCK_ERROR,
        "Error.")

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

    self._mock_api.start_job_update.assert_called_once_with(
        mock_config,
        self._mock_options.instance_spec.instance)

    self.assert_lock_message(self._fake_context)

  def test_update_cron_job_fails(self):
    mock_config = self.create_mock_config(is_cron=True)
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

  def test_update_no_active_instance_check(self):
    self._mock_options.instance_spec = TaskInstanceKey(self.TEST_JOBKEY, [1])
    self._mock_options.strict = True

    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = self.create_simple_success_response()

    self._command.execute(self._fake_context)

    self._mock_api.start_job_update.assert_called_once_with(
      mock_config,
      self._mock_options.instance_spec.instance)
class TestStartUpdateCommand(AuroraClientCommandTest):

  def setUp(self):
    self._command = StartUpdate()
    self._job_key = AuroraJobKey("cluster", "role", "env", "job")
    self._mock_options = mock_verb_options(self._command)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, [])
    self._fake_context = FakeAuroraCommandContext()
    self._fake_context.set_options(self._mock_options)
    self._mock_api = self._fake_context.get_api('UNUSED')

  @classmethod
  def create_mock_config(cls, is_cron=False):
    mock_config = create_autospec(spec=AuroraConfig, spec_set=True, instance=True)
    mock_raw_config = Mock()
    mock_raw_config.has_cron_schedule.return_value = is_cron
    mock_config.raw = Mock(return_value=mock_raw_config)
    return mock_config

  def test_start_update_with_lock(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
        ResponseCode.LOCK_ERROR,
        "Error.")

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

    self._mock_api.start_job_update.assert_called_once_with(
        mock_config,
        self._mock_options.instance_spec.instance)

    self.assert_lock_message(self._fake_context)

  def test_update_cron_job_fails(self):
    mock_config = self.create_mock_config(is_cron=True)
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

  def test_update_no_active_instance_check(self):
    self._mock_options.instance_spec = TaskInstanceKey(self.TEST_JOBKEY, [1])
    self._mock_options.strict = True

    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = self.create_simple_success_response()

    self._command.execute(self._fake_context)

    self._mock_api.start_job_update.assert_called_once_with(
      mock_config,
      self._mock_options.instance_spec.instance)
Exemple #7
0
class TestStartUpdate(AuroraClientCommandTest):
  def setUp(self):
    self._command = StartUpdate()
    self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
    self._mock_options = mock_verb_options(self._command)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
    self._mock_options.wait = False
    self._fake_context = FakeAuroraCommandContext()
    self._fake_context.set_options(self._mock_options)
    self._mock_api = self._fake_context.get_api('UNUSED')

  @classmethod
  def create_mock_config(cls, is_cron=False):
    # TODO(wfarner): Consider using a real AuroraConfig object for this.
    mock_config = create_autospec(spec=AuroraConfig, spec_set=True, instance=True)
    raw_config = Job(cron_schedule='something' if is_cron else Empty)
    mock_config.raw = Mock(return_value=raw_config)
    mock_config.cluster = Mock(return_value=cls.TEST_CLUSTER)
    return mock_config

  def test_start_update_with_lock(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
        ResponseCode.LOCK_ERROR,
        "Error.")

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance)
    ]
    self.assert_lock_message(self._fake_context)

  def test_update_cron_job_fails(self):
    mock_config = self.create_mock_config(is_cron=True)
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

  def test_update_no_active_instance_check(self):
    self._mock_options.instance_spec = TaskInstanceKey(self.TEST_JOBKEY, [1])
    self._mock_options.strict = True

    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = self.create_simple_success_response()

    self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance)
    ]

  def test_start_update_command_line_succeeds(self):
    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
      key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
    self._mock_options.message = 'hello'
    assert self._command.execute(self._fake_context) == EXIT_OK

    assert self._mock_api.start_job_update.mock_calls == [
        call(ANY, 'hello', None)
    ]
    assert self._fake_context.get_out() == [
        StartUpdate.UPDATE_MSG_TEMPLATE %
        ('http://something_or_other/scheduler/role/env/name/update/id')
    ]
    assert self._fake_context.get_err() == []

  def test_start_update_and_wait_success(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ROLLED_FORWARD)
    ]

    assert self._command.execute(self._fake_context) == EXIT_OK

    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None)]
    assert self._mock_api.query_job_updates.mock_calls == [
        call(update_key=resp.result.startJobUpdateResult.key)
    ]

    assert self._fake_context.get_out() == [
        StartUpdate.UPDATE_MSG_TEMPLATE %
        ('http://something_or_other/scheduler/role/env/name/update/id'),
        'Current state ROLLED_FORWARD'
    ]
    assert self._fake_context.get_err() == []

  def test_start_update_and_wait_rollback(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ROLLED_BACK)
    ]

    assert self._command.execute(self._fake_context) == EXIT_COMMAND_FAILURE

  def test_start_update_and_wait_error(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ERROR)
    ]

    assert self._command.execute(self._fake_context) == EXIT_UNKNOWN_ERROR

  def test_start_update_command_line_succeeds_noop_update(self):
    resp = self.create_simple_success_response()
    resp.details = [ResponseDetail(message="Noop update.")]
    self._mock_api.start_job_update.return_value = resp
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    result = self._command.execute(self._fake_context)
    assert result == EXIT_OK

    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None)]
    assert self._fake_context.get_out() == ["Noop update."]
    assert self._fake_context.get_err() == []

  def test_update_pulse_interval_too_small(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    error = Context.CommandError(100, 'something failed')
    self._mock_api.start_job_update.side_effect = error

    with pytest.raises(Context.CommandError) as e:
      self._command.execute(self._fake_context)

    assert e.value == error
    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None)]
Exemple #8
0
class TestStartUpdate(AuroraClientCommandTest):
  def setUp(self):
    self._command = StartUpdate()
    self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
    self._mock_options = mock_verb_options(self._command)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
    self._mock_options.wait = False
    self._fake_context = FakeAuroraCommandContext()
    self._fake_context.set_options(self._mock_options)
    self._mock_api = self._fake_context.get_api('UNUSED')
    self._formatter = Mock(spec=DiffFormatter)

  @classmethod
  def create_mock_config(cls, is_cron=False):
    # TODO(wfarner): Consider using a real AuroraConfig object for this.
    mock_config = create_autospec(spec=AuroraConfig, spec_set=True, instance=True)
    raw_config = Job(cron_schedule='something' if is_cron else Empty)
    mock_config.raw = Mock(return_value=raw_config)
    mock_config.cluster = Mock(return_value=cls.TEST_CLUSTER)
    return mock_config

  def test_start_update_with_lock(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
        ResponseCode.LOCK_ERROR,
        "Error.")

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      with pytest.raises(Context.CommandError):
        self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance, ANY)
    ]
    self.assert_lock_message(self._fake_context)

  def test_start_update_invalid_request(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    err_msg = "Error."
    self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
        ResponseCode.INVALID_REQUEST,
        err_msg)

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      with pytest.raises(Context.CommandError):
        self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance, ANY)
    ]
    assert self._fake_context.get_err() == [
      StartUpdate.FAILED_TO_START_UPDATE_ERROR_MSG, "\t%s" % err_msg
    ]

  def test_start_update_already_inprogress(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    update_id = 'some-mocked-uuid'

    err_msg = "Active updates exist for this job."
    return_value = AuroraClientCommandTest.create_start_job_update_result(
      ResponseCode.INVALID_REQUEST, err_msg, UPDATE_KEY, {Metadata(CLIENT_UPDATE_ID, update_id)})
    self._mock_api.start_job_update.return_value = return_value

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      with patch('apache.aurora.client.cli.update.uuid') as mock_uuid:
        mock_uuid.uuid4.return_value = update_id
        self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance,
             {CLIENT_UPDATE_ID: update_id})
    ]
    assert self._fake_context.get_out() == [
      StartUpdate.UPDATE_MSG_TEMPLATE %
      ('http://something_or_other/scheduler/bozo/test/hello/update/update_id'),
    ]
    assert self._fake_context.get_err() == []

  def test_start_update_different_update_inprogress(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    update_id = 'some-mocked-uuid'
    update_id_2 = 'some-other-mocked-uuid'

    err_msg = "Active updates exist for this job."
    return_value = AuroraClientCommandTest.create_start_job_update_result(
      ResponseCode.INVALID_REQUEST, err_msg, UPDATE_KEY, {Metadata(CLIENT_UPDATE_ID, update_id)})
    self._mock_api.start_job_update.return_value = return_value

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      with pytest.raises(Context.CommandError):
        with patch('apache.aurora.client.cli.update.uuid') as mock_uuid:
          mock_uuid.uuid4.return_value = update_id_2
          self._command.execute(self._fake_context)

    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance,
             {CLIENT_UPDATE_ID: update_id_2})
    ]
    assert self._fake_context.get_out() == []
    assert self._fake_context.get_err() == [
      StartUpdate.FAILED_TO_START_UPDATE_ERROR_MSG, "\t%s" % err_msg
    ]

  def test_update_cron_job_fails(self):
    mock_config = self.create_mock_config(is_cron=True)
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    with pytest.raises(Context.CommandError):
      self._command.execute(self._fake_context)

  def test_update_no_active_instance_check(self):
    self._mock_options.instance_spec = TaskInstanceKey(self.TEST_JOBKEY, [1])
    self._mock_options.strict = True

    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_api.start_job_update.return_value = self.create_simple_success_response()

    with patch('apache.aurora.client.cli.update.DiffFormatter') as formatter:
      formatter.return_value = self._formatter
      self._command.execute(self._fake_context)

    assert self._formatter.show_job_update_diff.mock_calls == [
      call(self._mock_options.instance_spec.instance)
    ]
    assert self._mock_api.start_job_update.mock_calls == [
        call(mock_config, None, self._mock_options.instance_spec.instance, ANY)
    ]

  def test_start_update_command_line_succeeds(self):
    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
      key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
    self._mock_options.message = 'hello'

    with patch('apache.aurora.client.cli.update.DiffFormatter') as formatter:
      formatter.return_value = self._formatter
      assert self._command.execute(self._fake_context) == EXIT_OK

    assert self._formatter.show_job_update_diff.mock_calls == [
      call(self._mock_options.instance_spec.instance)
    ]
    assert self._mock_api.start_job_update.mock_calls == [
        call(ANY, 'hello', None, ANY)
    ]
    assert self._fake_context.get_out() == [
      StartUpdate.UPDATE_MSG_TEMPLATE %
      ('http://something_or_other/scheduler/role/env/name/update/id'),
    ]
    assert self._fake_context.get_err() == []

  def test_start_update_and_wait_success(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ROLLED_FORWARD)
    ]

    with patch('apache.aurora.client.cli.update.DiffFormatter') as formatter:
      formatter.return_value = self._formatter
      assert self._command.execute(self._fake_context) == EXIT_OK

    assert self._formatter.show_job_update_diff.mock_calls == [
      call(self._mock_options.instance_spec.instance)
    ]
    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None, ANY)]
    assert self._mock_api.query_job_updates.mock_calls == [
      call(update_key=resp.result.startJobUpdateResult.key)
    ]

    assert self._fake_context.get_out() == [
      StartUpdate.UPDATE_MSG_TEMPLATE %
      ('http://something_or_other/scheduler/role/env/name/update/id'),
      'Current state ROLLED_FORWARD'
    ]
    assert self._fake_context.get_err() == []

  def test_start_update_and_wait_rollback(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ROLLED_BACK)
    ]

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      assert self._command.execute(self._fake_context) == EXIT_COMMAND_FAILURE

  def test_start_update_and_wait_error(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)
    self._mock_options.wait = True

    resp = self.create_simple_success_response()
    resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
        key=JobUpdateKey(job=JobKey(role="role", environment="env", name="name"), id="id")))
    self._mock_api.start_job_update.return_value = resp
    self._mock_api.query_job_updates.side_effect = [
        get_status_query_response(status=JobUpdateStatus.ERROR)
    ]

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      assert self._command.execute(self._fake_context) == EXIT_UNKNOWN_ERROR

  def test_start_update_command_line_succeeds_noop_update(self):
    resp = self.create_simple_success_response()
    resp.details = [ResponseDetail(message="Noop update.")]
    self._mock_api.start_job_update.return_value = resp
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    with patch('apache.aurora.client.cli.update.DiffFormatter') as formatter:
      formatter.return_value = self._formatter
      assert self._command.execute(self._fake_context) == EXIT_OK

    assert self._formatter.show_job_update_diff.mock_calls == [
      call(self._mock_options.instance_spec.instance)
    ]
    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None, ANY)]
    assert self._fake_context.get_out() == [
      "Noop update."
    ]
    assert self._fake_context.get_err() == []

  def test_update_pulse_interval_too_small(self):
    mock_config = self.create_mock_config()
    self._fake_context.get_job_config = Mock(return_value=mock_config)

    error = Context.CommandError(100, 'something failed')
    self._mock_api.start_job_update.side_effect = error

    with patch('apache.aurora.client.cli.update.DiffFormatter'):
      with pytest.raises(Context.CommandError) as e:
        self._command.execute(self._fake_context)

    assert e.value == error
    assert self._mock_api.start_job_update.mock_calls == [call(ANY, None, None, ANY)]
Exemple #9
0
class TestStartUpdate(AuroraClientCommandTest):
    def setUp(self):
        self._command = StartUpdate()
        self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
        self._mock_options = mock_verb_options(self._command)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._mock_options.wait = False
        self._fake_context = FakeAuroraCommandContext()
        self._fake_context.set_options(self._mock_options)
        self._mock_api = self._fake_context.get_api('UNUSED')
        self._formatter = Mock(spec=DiffFormatter)

    @classmethod
    def create_mock_config(cls, is_cron=False):
        # TODO(wfarner): Consider using a real AuroraConfig object for this.
        mock_config = create_autospec(spec=AuroraConfig,
                                      spec_set=True,
                                      instance=True)
        raw_config = Job(cron_schedule='something' if is_cron else Empty)
        mock_config.raw = Mock(return_value=raw_config)
        mock_config.cluster = Mock(return_value=cls.TEST_CLUSTER)
        return mock_config

    def test_start_update_with_lock(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
            ResponseCode.LOCK_ERROR, "Error.")

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            with pytest.raises(Context.CommandError):
                self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance,
                 ANY)
        ]
        self.assert_lock_message(self._fake_context)

    def test_start_update_invalid_request(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        err_msg = "Error."
        self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
            ResponseCode.INVALID_REQUEST, err_msg)

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            with pytest.raises(Context.CommandError):
                self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance,
                 ANY)
        ]
        assert self._fake_context.get_err() == [
            StartUpdate.FAILED_TO_START_UPDATE_ERROR_MSG,
            "\t%s" % err_msg
        ]

    def test_start_update_already_inprogress(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        update_id = 'some-mocked-uuid'

        err_msg = "Active updates exist for this job."
        return_value = AuroraClientCommandTest.create_start_job_update_result(
            ResponseCode.INVALID_REQUEST, err_msg, UPDATE_KEY,
            {Metadata(CLIENT_UPDATE_ID, update_id)})
        self._mock_api.start_job_update.return_value = return_value

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            with patch('apache.aurora.client.cli.update.uuid') as mock_uuid:
                mock_uuid.uuid4.return_value = update_id
                self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance,
                 {CLIENT_UPDATE_ID: update_id})
        ]
        assert self._fake_context.get_out() == [
            StartUpdate.UPDATE_MSG_TEMPLATE %
            ('http://something_or_other/scheduler/bozo/test/hello/update/update_id'
             ),
        ]
        assert self._fake_context.get_err() == []

    def test_start_update_different_update_inprogress(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        update_id = 'some-mocked-uuid'
        update_id_2 = 'some-other-mocked-uuid'

        err_msg = "Active updates exist for this job."
        return_value = AuroraClientCommandTest.create_start_job_update_result(
            ResponseCode.INVALID_REQUEST, err_msg, UPDATE_KEY,
            {Metadata(CLIENT_UPDATE_ID, update_id)})
        self._mock_api.start_job_update.return_value = return_value

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            with pytest.raises(Context.CommandError):
                with patch(
                        'apache.aurora.client.cli.update.uuid') as mock_uuid:
                    mock_uuid.uuid4.return_value = update_id_2
                    self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance,
                 {CLIENT_UPDATE_ID: update_id_2})
        ]
        assert self._fake_context.get_out() == []
        assert self._fake_context.get_err() == [
            StartUpdate.FAILED_TO_START_UPDATE_ERROR_MSG,
            "\t%s" % err_msg
        ]

    def test_update_cron_job_fails(self):
        mock_config = self.create_mock_config(is_cron=True)
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        with pytest.raises(Context.CommandError):
            self._command.execute(self._fake_context)

    def test_update_no_active_instance_check(self):
        self._mock_options.instance_spec = TaskInstanceKey(
            self.TEST_JOBKEY, [1])
        self._mock_options.strict = True

        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = self.create_simple_success_response(
        )

        with patch(
                'apache.aurora.client.cli.update.DiffFormatter') as formatter:
            formatter.return_value = self._formatter
            self._command.execute(self._fake_context)

        assert self._formatter.show_job_update_diff.mock_calls == [
            call(self._mock_options.instance_spec.instance)
        ]
        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance,
                 ANY)
        ]

    def test_start_update_command_line_succeeds(self):
        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._mock_options.message = 'hello'

        with patch(
                'apache.aurora.client.cli.update.DiffFormatter') as formatter:
            formatter.return_value = self._formatter
            assert self._command.execute(self._fake_context) == EXIT_OK

        assert self._formatter.show_job_update_diff.mock_calls == [
            call(self._mock_options.instance_spec.instance)
        ]
        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, 'hello', None, ANY)
        ]
        assert self._fake_context.get_out() == [
            StartUpdate.UPDATE_MSG_TEMPLATE %
            ('http://something_or_other/scheduler/role/env/name/update/id'),
        ]
        assert self._fake_context.get_err() == []

    def test_start_update_and_wait_success(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ROLLED_FORWARD)
        ]

        with patch(
                'apache.aurora.client.cli.update.DiffFormatter') as formatter:
            formatter.return_value = self._formatter
            assert self._command.execute(self._fake_context) == EXIT_OK

        assert self._formatter.show_job_update_diff.mock_calls == [
            call(self._mock_options.instance_spec.instance)
        ]
        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None, ANY)
        ]
        assert self._mock_api.query_job_updates.mock_calls == [
            call(update_key=resp.result.startJobUpdateResult.key)
        ]

        assert self._fake_context.get_out() == [
            StartUpdate.UPDATE_MSG_TEMPLATE %
            ('http://something_or_other/scheduler/role/env/name/update/id'),
            'Current state ROLLED_FORWARD'
        ]
        assert self._fake_context.get_err() == []

    def test_start_update_and_wait_rollback(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ROLLED_BACK)
        ]

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            assert self._command.execute(
                self._fake_context) == EXIT_COMMAND_FAILURE

    def test_start_update_and_wait_error(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ERROR)
        ]

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            assert self._command.execute(
                self._fake_context) == EXIT_UNKNOWN_ERROR

    def test_start_update_command_line_succeeds_noop_update(self):
        resp = self.create_simple_success_response()
        resp.details = [ResponseDetail(message="Noop update.")]
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        with patch(
                'apache.aurora.client.cli.update.DiffFormatter') as formatter:
            formatter.return_value = self._formatter
            assert self._command.execute(self._fake_context) == EXIT_OK

        assert self._formatter.show_job_update_diff.mock_calls == [
            call(self._mock_options.instance_spec.instance)
        ]
        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None, ANY)
        ]
        assert self._fake_context.get_out() == ["Noop update."]
        assert self._fake_context.get_err() == []

    def test_update_pulse_interval_too_small(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        error = Context.CommandError(100, 'something failed')
        self._mock_api.start_job_update.side_effect = error

        with patch('apache.aurora.client.cli.update.DiffFormatter'):
            with pytest.raises(Context.CommandError) as e:
                self._command.execute(self._fake_context)

        assert e.value == error
        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None, ANY)
        ]
Exemple #10
0
class TestStartUpdate(AuroraClientCommandTest):
    def setUp(self):
        self._command = StartUpdate()
        self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
        self._mock_options = mock_verb_options(self._command)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._mock_options.wait = False
        self._fake_context = FakeAuroraCommandContext()
        self._fake_context.set_options(self._mock_options)
        self._mock_api = self._fake_context.get_api('UNUSED')

    @classmethod
    def create_mock_config(cls, is_cron=False):
        # TODO(wfarner): Consider using a real AuroraConfig object for this.
        mock_config = create_autospec(spec=AuroraConfig,
                                      spec_set=True,
                                      instance=True)
        raw_config = Job(cron_schedule='something' if is_cron else Empty)
        mock_config.raw = Mock(return_value=raw_config)
        mock_config.cluster = Mock(return_value=cls.TEST_CLUSTER)
        return mock_config

    def test_start_update_with_lock(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
            ResponseCode.LOCK_ERROR, "Error.")

        with pytest.raises(Context.CommandError):
            self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance)
        ]
        self.assert_lock_message(self._fake_context)

    def test_update_cron_job_fails(self):
        mock_config = self.create_mock_config(is_cron=True)
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        with pytest.raises(Context.CommandError):
            self._command.execute(self._fake_context)

    def test_update_no_active_instance_check(self):
        self._mock_options.instance_spec = TaskInstanceKey(
            self.TEST_JOBKEY, [1])
        self._mock_options.strict = True

        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = self.create_simple_success_response(
        )

        self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance)
        ]

    def test_start_update_command_line_succeeds(self):
        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._mock_options.message = 'hello'
        assert self._command.execute(self._fake_context) == EXIT_OK

        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, 'hello', None)
        ]
        assert self._fake_context.get_out() == [
            StartUpdate.UPDATE_MSG_TEMPLATE %
            ('http://something_or_other/scheduler/role/env/name/update/id')
        ]
        assert self._fake_context.get_err() == []

    def test_start_update_and_wait_success(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ROLLED_FORWARD)
        ]

        assert self._command.execute(self._fake_context) == EXIT_OK

        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None)
        ]
        assert self._mock_api.query_job_updates.mock_calls == [
            call(update_key=resp.result.startJobUpdateResult.key)
        ]

        assert self._fake_context.get_out() == [
            StartUpdate.UPDATE_MSG_TEMPLATE %
            ('http://something_or_other/scheduler/role/env/name/update/id'),
            'Current state ROLLED_FORWARD'
        ]
        assert self._fake_context.get_err() == []

    def test_start_update_and_wait_rollback(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ROLLED_BACK)
        ]

        assert self._command.execute(
            self._fake_context) == EXIT_COMMAND_FAILURE

    def test_start_update_and_wait_error(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.wait = True

        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        self._mock_api.query_job_updates.side_effect = [
            get_status_query_response(status=JobUpdateStatus.ERROR)
        ]

        assert self._command.execute(self._fake_context) == EXIT_UNKNOWN_ERROR

    def test_start_update_command_line_succeeds_noop_update(self):
        resp = self.create_simple_success_response()
        resp.details = [ResponseDetail(message="Noop update.")]
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        result = self._command.execute(self._fake_context)
        assert result == EXIT_OK

        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None)
        ]
        assert self._fake_context.get_out() == ["Noop update."]
        assert self._fake_context.get_err() == []

    def test_update_pulse_interval_too_small(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        error = Context.CommandError(100, 'something failed')
        self._mock_api.start_job_update.side_effect = error

        with pytest.raises(Context.CommandError) as e:
            self._command.execute(self._fake_context)

        assert e.value == error
        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None)
        ]
Exemple #11
0
class TestStartUpdate(AuroraClientCommandTest):
    def setUp(self):
        self._command = StartUpdate()
        self._job_key = AuroraJobKey.from_thrift("cluster", UPDATE_KEY.job)
        self._mock_options = mock_verb_options(self._command)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._fake_context = FakeAuroraCommandContext()
        self._fake_context.set_options(self._mock_options)
        self._mock_api = self._fake_context.get_api('UNUSED')

    @classmethod
    def create_mock_config(cls, is_cron=False):
        mock_config = create_autospec(spec=AuroraConfig,
                                      spec_set=True,
                                      instance=True)
        raw_config = Job(cron_schedule='something' if is_cron else Empty)
        mock_config.raw = Mock(return_value=raw_config)
        mock_config.cluster = Mock(return_value=cls.TEST_CLUSTER)
        return mock_config

    def test_start_update_with_lock(self):
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = AuroraClientCommandTest.create_blank_response(
            ResponseCode.LOCK_ERROR, "Error.")

        with pytest.raises(Context.CommandError):
            self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance)
        ]
        self.assert_lock_message(self._fake_context)

    def test_update_cron_job_fails(self):
        mock_config = self.create_mock_config(is_cron=True)
        self._fake_context.get_job_config = Mock(return_value=mock_config)

        with pytest.raises(Context.CommandError):
            self._command.execute(self._fake_context)

    def test_update_no_active_instance_check(self):
        self._mock_options.instance_spec = TaskInstanceKey(
            self.TEST_JOBKEY, [1])
        self._mock_options.strict = True

        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_api.start_job_update.return_value = self.create_simple_success_response(
        )

        self._command.execute(self._fake_context)

        assert self._mock_api.start_job_update.mock_calls == [
            call(mock_config, None, self._mock_options.instance_spec.instance)
        ]

    def test_start_update_command_line_succeeds(self):
        resp = self.create_simple_success_response()
        resp.result = Result(startJobUpdateResult=StartJobUpdateResult(
            key=JobUpdateKey(job=JobKey(
                role="role", environment="env", name="name"),
                             id="id")))
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        self._mock_options.instance_spec = TaskInstanceKey(self._job_key, None)
        self._mock_options.message = 'hello'
        assert self._command.execute(self._fake_context) == EXIT_OK

        update_url_msg = StartUpdate.UPDATE_MSG_TEMPLATE % (
            'http://something_or_other/scheduler/role/env/name/id')

        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, 'hello', None)
        ]
        assert self._fake_context.get_out() == [update_url_msg]
        assert self._fake_context.get_err() == []

    def test_start_update_command_line_succeeds_noop_update(self):
        resp = self.create_simple_success_response()
        resp.details = [ResponseDetail(message="Noop update.")]
        self._mock_api.start_job_update.return_value = resp
        mock_config = self.create_mock_config()
        self._fake_context.get_job_config = Mock(return_value=mock_config)
        result = self._command.execute(self._fake_context)
        assert result == EXIT_OK

        assert self._mock_api.start_job_update.mock_calls == [
            call(ANY, None, None)
        ]
        assert self._fake_context.get_out() == ["Noop update."]
        assert self._fake_context.get_err() == []