Exemplo n.º 1
0
 def test_retry_false(self):
     """Tests correct behavior when retry is passed as false."""
     seq = [
         (self.update_call.intent, lambda _: (StubResponse(202, ''), None)),
         (Log('request-update-stack', ANY), lambda _: None)
     ]
     update = UpdateStack(stack=self.stack, stack_config=self.config,
                          retry=False)
     result = perform_sequence(seq, update.as_effect())
     self.assertEqual(result, (StepResult.SUCCESS, []))
Exemplo n.º 2
0
 def test_retry_default(self):
     """Tests correct behavior when retry is not specified."""
     seq = [
         (self.update_call.intent, lambda _: (StubResponse(202, ''), None)),
         (Log('request-update-stack', ANY), lambda _: None)
     ]
     update = UpdateStack(stack=self.stack, stack_config=self.config)
     reason = 'Waiting for stack to update'
     result = perform_sequence(seq, update.as_effect())
     self.assertEqual(result,
                      (StepResult.RETRY, [ErrorReason.String(reason)]))
Exemplo n.º 3
0
 def get_fix_steps(scale_down_steps):
     num_stacks_to_update = len(stacks_check_failed) - len(scale_down_steps)
     stacks_to_update = (stacks_check_failed[:num_stacks_to_update]
                         if num_stacks_to_update > 0 else [])
     return [UpdateStack(stack=s, stack_config=config)
             for s in stacks_to_update]
Exemplo n.º 4
0
def converge_launch_stack(desired_state, stacks):
    """
    Create steps that indicate how to transition from the state provided
    by the given parameters to the :obj:`DesiredStackGroupState` described by
    ``desired_state``.

    See note [Converging stacks] for more information.

    :param DesiredStackGroupState desired_state: The desired group state.
    :param set stacks: a set of :obj:`HeatStack` instances.
        This must only contain stacks that are being managed for the specified
        group.
    :rtype: :obj:`pbag` of `IStep`

    """
    config = desired_state.stack_config

    by_state = groupby(lambda stack: stack.get_state(), stacks)

    stacks_complete = by_state.get(StackState.CREATE_UPDATE_COMPLETE, [])
    stacks_failed = by_state.get(StackState.CREATE_UPDATE_FAILED, [])
    stacks_check_complete = by_state.get(StackState.CHECK_COMPLETE, [])
    stacks_check_failed = by_state.get(StackState.CHECK_FAILED, [])
    stacks_in_progress = by_state.get(StackState.IN_PROGRESS, [])
    stacks_delete_in_progress = by_state.get(StackState.DELETE_IN_PROGRESS, [])
    stacks_delete_failed = by_state.get(StackState.DELETE_FAILED, [])

    stacks_good = stacks_complete + stacks_check_complete
    stacks_amiss = (stacks_failed +
                    stacks_check_failed +
                    stacks_in_progress +
                    stacks_delete_in_progress)

    if stacks_delete_failed:
        reasons = [ErrorReason.String("Stacks in DELETE_FAILED found.")]
        return pbag([FailConvergence(reasons)])

    # If there are no stacks in CHECK_* or other work to be done, we assume
    # we're at the beginning of a convergence cycle and need to perform stack
    # checks.
    if stacks_complete and not (stacks_check_complete or stacks_amiss):
        return pbag([CheckStack(stack) for stack in stacks_complete])

    # Otherwise, if all stacks are in a good state and we have the right number
    # of stacks, we call update on the stacks in CHECK_COMPLETE and return
    # SUCCESS without waiting for it to finish (calling update on a stack in
    # CREATE_COMPLETE is essentially a no-op) so that there will be no stacks
    # in CREATE_* the next time otter tries to converge this group. This will
    # cause all of the stacks to be checked at that time and let otter know
    # if there are any stacks that have fallen into an error state.
    elif not stacks_amiss and len(stacks_good) == desired_state.capacity:
        return pbag([UpdateStack(stack=stack, stack_config=config, retry=False)
                     for stack in stacks_check_complete])

    def get_create_steps():
        create_stack = CreateStack(stack_config=config)
        good_or_fixable_stack_count = (len(stacks_good) +
                                       len(stacks_in_progress) +
                                       len(stacks_check_failed))
        return [create_stack] * (desired_state.capacity -
                                 good_or_fixable_stack_count)

    def get_scale_down_steps():
        stacks_in_preferred_order = (
            stacks_good + stacks_in_progress + stacks_check_failed)
        unneeded_stacks = stacks_in_preferred_order[desired_state.capacity:]
        return map(DeleteStack, unneeded_stacks)

    def get_fix_steps(scale_down_steps):
        num_stacks_to_update = len(stacks_check_failed) - len(scale_down_steps)
        stacks_to_update = (stacks_check_failed[:num_stacks_to_update]
                            if num_stacks_to_update > 0 else [])
        return [UpdateStack(stack=s, stack_config=config)
                for s in stacks_to_update]

    create_steps = get_create_steps()
    scale_down_steps = get_scale_down_steps()
    fix_steps = get_fix_steps(scale_down_steps)
    delete_stacks_failed_steps = map(DeleteStack, stacks_failed)

    converge_later = (
        [ConvergeLater([ErrorReason.String("Waiting for stacks to finish.")])]
        if stacks_delete_in_progress or stacks_in_progress
        else [])

    return pbag(create_steps +
                fix_steps +
                scale_down_steps +
                delete_stacks_failed_steps +
                converge_later)
Exemplo n.º 5
0
 def test_normal_use(self):
     """Tests normal usage."""
     update = UpdateStack(stack=self.stack, stack_config=self.config)
     self.assertEqual(update.as_effect().intent,
                      self.update_call.intent)