def test_on_cleanup_error__may_be_called_several_times_per_cleanup(self):
        def bad_cleanup1():
            raise RuntimeError("CLEANUP_1")

        def bad_cleanup2():
            raise RuntimeError("CLEANUP_2")

        class CleanupErrorCollector(object):
            def __init__(self):
                self.collected = []

            def __call__(self, context, cleanup_func, exception):
                self.collected.append((context, cleanup_func, exception))

        context = Context(runner=Mock())
        collect_cleanup_error = CleanupErrorCollector()
        with pytest.raises(RuntimeError):
            with scoped_context_layer(context):
                context.on_cleanup_error = collect_cleanup_error
                context.add_cleanup(bad_cleanup1)
                context.add_cleanup(bad_cleanup2)

        expected = [
            (context, bad_cleanup2, RuntimeError("CLEANUP_2")),
            (context, bad_cleanup1, RuntimeError("CLEANUP_1")),
        ]
        assert len(collect_cleanup_error.collected) == 2
        assert collect_cleanup_error.collected[0][:-1] == expected[0][:-1]
        assert collect_cleanup_error.collected[1][:-1] == expected[1][:-1]
def launch_lxd_container(
    context: Context, image_name: str, container_name: str
) -> None:
    """Launch a container from an image and wait for it to boot

    This will also register a cleanup with behave so the container will be
    removed before test execution completes.

    :param context:
        A `behave.runner.Context`; used only for registering cleanups.

    :param container_name:
        The name to be used for the launched container.
    """
    subprocess.run(["lxc", "launch", image_name, container_name])

    def cleanup_container() -> None:
        if not context.config.destroy_instances:
            print("Leaving lxd container running: {}".format(container_name))
        else:
            subprocess.run(["lxc", "delete", "-f", container_name])

    context.add_cleanup(cleanup_container)

    wait_for_boot(container_name)
Exemple #3
0
def create_trusty_uat_lxd_image(context: Context) -> None:
    """Create a trusty lxd image with ubuntu-advantage-tools installed

    This will launch a container, install ubuntu-advantage-tools, and publish
    the image.  The image's name is stored in context.image_name for use within
    step code.

    :param context:
        A `behave.runner.Context`; this will have `image_name` set on it.
    """
    def image_cleanup() -> None:
        if context.config.image_clean:
            subprocess.run(["lxc", "image", "delete", context.image_name])
        else:
            print("Image cleanup disabled, not deleting:", context.image_name)

    if context.reuse_container:
        print(" Reusing the existent container: ", context.reuse_container)
    else:
        now = datetime.datetime.now()
        context.image_name = "behave-image-" + now.strftime("%s%f")
        build_container_name = "behave-image-build-" + now.strftime("%s%f")
        launch_lxd_container(context, "ubuntu:trusty", build_container_name)
        _install_uat_in_container(build_container_name)
        _capture_container_as_image(build_container_name, context.image_name)
        context.add_cleanup(image_cleanup)
Exemple #4
0
 def test_add_cleanup_with_known_layer_and_kwargs(self):
     my_cleanup = Mock(spec=cleanup_func_with_args)
     context = Context(runner=Mock())
     with scoped_context_layer(context, layer="scenario"):
         context.add_cleanup(my_cleanup, layer="scenario", name="alice")
         my_cleanup.assert_not_called()
     # CALLS-HERE: context._pop()
     my_cleanup.assert_called_once_with(name="alice")
Exemple #5
0
 def test_add_cleanup_with_known_layer(self):
     my_cleanup = Mock(spec=cleanup_func)
     context = Context(runner=Mock())
     with scoped_context_layer(context, layer="scenario"):
         context.add_cleanup(my_cleanup, layer="scenario")
         my_cleanup.assert_not_called()
     # CALLS-HERE: context._pop()
     my_cleanup.assert_called_once()
Exemple #6
0
 def test_add_cleanup_with_args(self):
     my_cleanup = Mock(spec=cleanup_func_with_args)
     context = Context(runner=Mock())
     with scoped_context_layer(context):
         context.add_cleanup(my_cleanup, 1, 2, 3)
         my_cleanup.assert_not_called()
     # CALLS-HERE: context._pop()
     my_cleanup.assert_called_once_with(1, 2, 3)
Exemple #7
0
 def test_add_cleanup_with_unknown_layer_raises_lookup_error(self):
     """Cleanup function is not registered"""
     my_cleanup = Mock(spec=cleanup_func)
     context = Context(runner=Mock())
     with scoped_context_layer(context):  # CALLS-HERE: context._push()
         with pytest.raises(LookupError) as error:
             context.add_cleanup(my_cleanup, layer="other")
     my_cleanup.assert_not_called()
 def test_cleanup_func_is_called_when_context_frame_is_popped(self):
     my_cleanup = Mock(spec=cleanup_func)
     context = Context(runner=Mock())
     with scoped_context_layer(context):  # CALLS-HERE: context._push()
         context.add_cleanup(my_cleanup)
         # -- ENSURE: Not called before context._pop()
         my_cleanup.assert_not_called()
     # CALLS-HERE: context._pop()
     my_cleanup.assert_called_once()
Exemple #9
0
 def test_add_cleanup_with_known_deeper_layer3(self):
     my_cleanup = Mock(spec=cleanup_func)
     context = Context(runner=Mock())
     with scoped_context_layer(context, layer="testrun"):
         with scoped_context_layer(context, layer="feature"):
             with scoped_context_layer(context, layer="scenario"):
                 context.add_cleanup(my_cleanup, layer="feature")
             my_cleanup.assert_not_called()
         my_cleanup.assert_called_once()  # LEFT: layer="feature"
     my_cleanup.assert_called_once()
    def test_add_cleanup__rejects_noncallable_cleanup_func(self):
        class NonCallable(object):
            pass

        non_callable = NonCallable()
        context = Context(runner=Mock())

        with pytest.raises(AssertionError) as e:
            with scoped_context_layer(context):
                context.add_cleanup(non_callable)
        assert "REQUIRES: callable(cleanup_func)" in str(e.value)
Exemple #11
0
def launch_lxd_container(
    context: Context,
    series: str,
    image_name: str,
    container_name: str,
    is_vm: bool,
) -> None:
    """Launch a container from an image and wait for it to boot

    This will also register a cleanup with behave so the container will be
    removed before test execution completes.

    :param context:
        A `behave.runner.Context`; used only for registering cleanups.
    :param image_name:
        The name of the lxd image to launch as base image for the container
    :param container_name:
        The name to be used for the launched container.
    :param series: A string representing the series of the vm to create
    :param is_vm:
        Boolean as to whether or not to launch KVM type container
    :param user_data:
        Optional str of userdata to pass to the launched image
    """
    command = ["lxc", "launch", image_name, container_name]
    if is_vm:
        lxc_create_vm_profile(series)
        command.extend(["--profile", VM_PROFILE_TMPL.format(series), "--vm"])
    subprocess.check_call(command)

    if is_vm:
        """ When we publish vm images we end up losing the image information.
        Since we need at least the release information to reuse the vm instance
        in other tests, we are adding this information back here."""
        subprocess.run(["lxc", "stop", container_name])
        subprocess.run(
            ["lxc", "config", "set", container_name, "image.release", series]
        )
        subprocess.run(["lxc", "start", container_name])

    def cleanup_container() -> None:
        if not context.config.destroy_instances:
            print(
                "--- Leaving lxd container running: {}".format(container_name)
            )
        else:
            subprocess.run(["lxc", "delete", "-f", container_name])

    context.add_cleanup(cleanup_container)

    wait_for_boot(container_name, series=series, is_vm=is_vm)
    def test_on_cleanup_error__is_called_if_defined(self):
        def bad_cleanup():
            raise RuntimeError("in CLEANUP call")

        def handle_cleanup_error(context, cleanup_func, exception):
            print("CALLED: handle_cleanup_error")

        context = Context(runner=Mock())
        handle_cleanup_error_func = Mock(spec=handle_cleanup_error)
        with pytest.raises(RuntimeError):
            with scoped_context_layer(context):
                context.on_cleanup_error = handle_cleanup_error_func
                context.add_cleanup(bad_cleanup)

        handle_cleanup_error_func.assert_called_once()
    def test_cleanup_funcs_are_called_when_context_frame_is_popped(self):
        my_cleanup1 = Mock(spec=cleanup_func)
        my_cleanup2 = Mock(spec=cleanup_func)

        # -- SETUP:
        context = Context(runner=Mock())
        with scoped_context_layer(context):
            context.add_cleanup(my_cleanup1)
            context.add_cleanup(my_cleanup2)

            # -- ENSURE: Not called before context._pop()
            my_cleanup1.assert_not_called()
            my_cleanup2.assert_not_called()
        # -- CALLS-HERE: context._pop()
        my_cleanup1.assert_called_once()
        my_cleanup2.assert_called_once()
    def test_on_cleanup_error__prints_error_by_default(self, capsys):
        def bad_cleanup_func():
            raise RuntimeError("in CLEANUP call")

        bad_cleanup = Mock(side_effect=bad_cleanup_func)

        context = Context(runner=Mock())
        with pytest.raises(RuntimeError):
            with scoped_context_layer(context):
                context.add_cleanup(bad_cleanup)

        captured_output, _ = capsys.readouterr()
        bad_cleanup.assert_called()
        assert "CLEANUP-ERROR in " in captured_output
        assert "RuntimeError: in CLEANUP call" in captured_output
        # -- FOR DIAGNOSTICS:
        print(captured_output)
    def test_cleanup_funcs_are_called_in_reversed_order(self):
        call_listener = CallListener()
        my_cleanup1A = CleanupFunction("CLEANUP1", listener=call_listener)
        my_cleanup2A = CleanupFunction("CLEANUP2", listener=call_listener)
        my_cleanup1 = Mock(side_effect=my_cleanup1A)
        my_cleanup2 = Mock(side_effect=my_cleanup2A)

        # -- SETUP:
        context = Context(runner=Mock())
        with scoped_context_layer(context):
            context.add_cleanup(my_cleanup1)
            context.add_cleanup(my_cleanup2)
            my_cleanup1.assert_not_called()
            my_cleanup2.assert_not_called()

        # -- ENSURE: Reversed order of cleanup calls
        expected_call_order = ["called:CLEANUP2", "called:CLEANUP1"]
        assert call_listener.collected == expected_call_order
        my_cleanup1.assert_called_once()
        my_cleanup2.assert_called_once()
    def test_cleanup_funcs_on_two_context_frames(self):
        call_listener = CallListener()
        my_cleanup_A1 = CleanupFunction("CLEANUP_A1", listener=call_listener)
        my_cleanup_A2 = CleanupFunction("CLEANUP_A2", listener=call_listener)
        my_cleanup_B1 = CleanupFunction("CLEANUP_B1", listener=call_listener)
        my_cleanup_B2 = CleanupFunction("CLEANUP_B2", listener=call_listener)
        my_cleanup_B3 = CleanupFunction("CLEANUP_B3", listener=call_listener)
        my_cleanup_A1M = Mock(side_effect=my_cleanup_A1)
        my_cleanup_A2M = Mock(side_effect=my_cleanup_A2)
        my_cleanup_B1M = Mock(side_effect=my_cleanup_B1)
        my_cleanup_B2M = Mock(side_effect=my_cleanup_B2)
        my_cleanup_B3M = Mock(side_effect=my_cleanup_B3)

        # -- SETUP:
        context = Context(runner=Mock())
        with scoped_context_layer(context):  # -- LAYER A:
            context.add_cleanup(my_cleanup_A1M)
            context.add_cleanup(my_cleanup_A2M)

            with scoped_context_layer(context):  # -- LAYER B:
                context.add_cleanup(my_cleanup_B1M)
                context.add_cleanup(my_cleanup_B2M)
                context.add_cleanup(my_cleanup_B3M)
                my_cleanup_B1M.assert_not_called()
                my_cleanup_B2M.assert_not_called()
                my_cleanup_B3M.assert_not_called()
            # -- context.pop(LAYER_B): Call cleanups for Bx
            expected_call_order = [
                "called:CLEANUP_B3",
                "called:CLEANUP_B2",
                "called:CLEANUP_B1",
            ]
            assert call_listener.collected == expected_call_order
            my_cleanup_A1M.assert_not_called()
            my_cleanup_A2M.assert_not_called()
            my_cleanup_B1M.assert_called_once()
            my_cleanup_B2M.assert_called_once()
            my_cleanup_B3M.assert_called_once()
        # -- context.pop(LAYER_A): Call cleanups for Ax
        expected_call_order = [
            "called:CLEANUP_B3",
            "called:CLEANUP_B2",
            "called:CLEANUP_B1",
            "called:CLEANUP_A2",
            "called:CLEANUP_A1",
        ]
        assert call_listener.collected == expected_call_order
        my_cleanup_A1M.assert_called_once()
        my_cleanup_A2M.assert_called_once()
        my_cleanup_B1M.assert_called_once()
        my_cleanup_B2M.assert_called_once()
        my_cleanup_B3M.assert_called_once()
def step_impl(context: Context, stack_name):
    """Add this as a given to ensure that the template bucket is cleaned up before we attempt to
    delete it; Otherwise, it will fail since you can't delete a bucket with objects in it.
    """
    context.add_cleanup(cleanup_template_files_in_bucket, context.sceptre_dir,
                        stack_name)
def step_impl(context: Context):
    placeholder_context = use_resolver_placeholders_on_error()
    placeholder_context.__enter__()
    context.add_cleanup(exit_placeholder_context, placeholder_context)