<commit_before>#!/usr/bin/env python

from argparse import ArgumentParser
from contextlib import contextmanager
import logging
from textwrap import dedent
from subprocess import CalledProcessError
import sys

from jujupy import (
    EnvJujuClient,
    SimpleEnvironment,
    until_timeout,
    )
from deploy_stack import (
    BootstrapManager,
    check_token,
    get_random_string,
    )
from jujuci import add_credential_args
from utility import (
    configure_logging,
    local_charm_path,
)


def prepare_dummy_env(client):
    """Use a client to prepare a dummy environment."""
    charm_source = local_charm_path(
        charm='dummy-source', juju_ver=client.version)
    client.deploy(charm_source)
    charm_sink = local_charm_path(charm='dummy-sink', juju_ver=client.version)
    client.deploy(charm_sink)
    token = get_random_string()
    client.set_config('dummy-source', {'token': token})
    client.juju('add-relation', ('dummy-source', 'dummy-sink'))
    client.juju('expose', ('dummy-sink',))
    return token


def get_clients(initial, other, base_env, debug, agent_url):
    """Return the clients to use for testing."""
    environment = SimpleEnvironment.from_config(base_env)
    if agent_url is None:
        environment.config.pop('tools-metadata-url', None)
    initial_client = EnvJujuClient.by_version(environment, initial,
                                              debug=debug)
    other_client = EnvJujuClient.by_version(environment, other, debug=debug)
    # System juju is assumed to be released and the best choice for tearing
    # down environments reliably.  (For example, 1.18.x cannot tear down
    # environments with alpha agent-versions.)
    released_client = EnvJujuClient.by_version(environment, debug=debug)
    if released_client.env != initial_client.env:
        released_client = initial_client
    return initial_client, other_client, released_client


def assess_heterogeneous(initial, other, base_env, environment_name, log_dir,
                         upload_tools, debug, agent_url, agent_stream, series):
    """Top level function that prepares the clients and environment.

    initial and other are paths to the binary used initially, and a binary
    used later.  base_env is the name of the environment to base the
    environment on and environment_name is the new name for the environment.
    """
    initial_client, other_client, teardown_client = get_clients(
        initial, other, base_env, debug, agent_url)
    jes_enabled = initial_client.is_jes_enabled()
    bs_manager = BootstrapManager(
        environment_name, initial_client, teardown_client,
        bootstrap_host=None, machines=[], series=series, agent_url=agent_url,
        agent_stream=agent_stream, region=None, log_dir=log_dir,
        keep_env=False, permanent=jes_enabled, jes_enabled=jes_enabled)
    test_control_heterogeneous(bs_manager, other_client, upload_tools)


@contextmanager
def run_context(bs_manager, other, upload_tools):
    try:
        bs_manager.keep_env = True
        with bs_manager.booted_context(upload_tools):
            if other.env.juju_home != bs_manager.client.env.juju_home:
                raise AssertionError('Juju home out of sync')
            yield
        # Test clean shutdown of an environment.
        callback_with_fallback(other, bs_manager.tear_down_client,
                               nice_tear_down)
    except:
        bs_manager.tear_down()
        raise


def test_control_heterogeneous(bs_manager, other, upload_tools):
    """Test if one binary can control an environment set up by the other."""
    initial = bs_manager.client
    released = bs_manager.tear_down_client
    with run_context(bs_manager, other, upload_tools):
        token = prepare_dummy_env(initial)
        initial.wait_for_started()
        if sys.platform != "win32":
            # Currently, juju ssh is not working on Windows.
            check_token(initial, token)
            check_series(other)
            other.juju('run', ('--all', 'uname -a'))
        other.get_config('dummy-source')
        other.get_model_config()
        other.juju('remove-relation', ('dummy-source', 'dummy-sink'))
        status = other.get_status()
        other.juju('unexpose', ('dummy-sink',))
        status = other.get_status()
        if status.status['services']['dummy-sink']['exposed']:
            raise AssertionError('dummy-sink is still exposed')
        status = other.get_status()
        charm_path = local_charm_path(
            charm='dummy-sink', juju_ver=other.version)
        juju_with_fallback(other, released, 'deploy',
                           (charm_path, 'sink2'))
        other.wait_for_started()
        other.juju('add-relation', ('dummy-source', 'sink2'))
        status = other.get_status()
        other.juju('expose', ('sink2',))
        status = other.get_status()
        if 'sink2' not in status.status['services']:
            raise AssertionError('Sink2 missing')
        other.remove_service('sink2')
        for ignored in until_timeout(30):
            status = other.get_status()
            if 'sink2' not in status.status['services']:
                break
        else:
            raise AssertionError('Sink2 not destroyed')
        other.juju('add-relation', ('dummy-source', 'dummy-sink'))
        status = other.get_status()
        relations = status.status['services']['dummy-sink']['relations']
        if not relations['source'] == ['dummy-source']:
            raise AssertionError('source is not dummy-source.')
        other.juju('expose', ('dummy-sink',))
        status = other.get_status()
        if not status.status['services']['dummy-sink']['exposed']:
            raise AssertionError('dummy-sink is not exposed')
        other.juju('add-unit', ('dummy-sink',))
        if not has_agent(other, 'dummy-sink/1'):
            raise AssertionError('dummy-sink/1 was not added.')
        other.juju('remove-unit', ('dummy-sink/1',))
        status = other.get_status()
        if has_agent(other, 'dummy-sink/1'):
            raise AssertionError('dummy-sink/1 was not removed.')
        other.juju('add-machine', ('lxc',))
        status = other.get_status()
        lxc_machine, = set(k for k, v in status.agent_items() if
                           k.endswith('/lxc/0'))
        lxc_holder = lxc_machine.split('/')[0]
        other.juju('remove-machine', (lxc_machine,))
        wait_until_removed(other, lxc_machine)
        other.juju('remove-machine', (lxc_holder,))
        wait_until_removed(other, lxc_holder)

# suppress nosetests
test_control_heterogeneous.__test__ = False


def juju_with_fallback(other, released, command, args, include_e=True):<commit_msg>Support LXD and 2.0<commit_after>#!/usr/bin/env python

from argparse import ArgumentParser
from contextlib import contextmanager
import logging
from textwrap import dedent
from subprocess import CalledProcessError
import sys

from jujupy import (
    EnvJujuClient,
    EnvJujuClient1X,
    LXC_MACHINE,
    SimpleEnvironment,
    until_timeout,
    )
from deploy_stack import (
    BootstrapManager,
    check_token,
    get_random_string,
    )
from jujuci import add_credential_args
from utility import (
    configure_logging,
    local_charm_path,
)


def prepare_dummy_env(client):
    """Use a client to prepare a dummy environment."""
    charm_source = local_charm_path(
        charm='dummy-source', juju_ver=client.version)
    client.deploy(charm_source)
    charm_sink = local_charm_path(charm='dummy-sink', juju_ver=client.version)
    client.deploy(charm_sink)
    token = get_random_string()
    client.set_config('dummy-source', {'token': token})
    client.juju('add-relation', ('dummy-source', 'dummy-sink'))
    client.juju('expose', ('dummy-sink',))
    return token


def get_clients(initial, other, base_env, debug, agent_url):
    """Return the clients to use for testing."""
    environment = SimpleEnvironment.from_config(base_env)
    if agent_url is None:
        environment.config.pop('tools-metadata-url', None)
    initial_client = EnvJujuClient.by_version(environment, initial,
                                              debug=debug)
    other_client = EnvJujuClient.by_version(initial_client.env, other,
                                            debug=debug)
    # System juju is assumed to be released and the best choice for tearing
    # down environments reliably.  (For example, 1.18.x cannot tear down
    # environments with alpha agent-versions.)
    released_client = EnvJujuClient.by_version(environment, debug=debug)
    # If released_client is a different major version, it cannot tear down
    # initial client, so use initial client for teardown.
    if (isinstance(released_client, EnvJujuClient1X) !=
        isinstance(initial_client, EnvJujuClient1X)):
            released_client = initial_client
    else:
        # If system juju is used, ensure it has identical env to
        # initial_client.
        released_client.env = initial_client.env
    return initial_client, other_client, released_client


def assess_heterogeneous(initial, other, base_env, environment_name, log_dir,
                         upload_tools, debug, agent_url, agent_stream, series):
    """Top level function that prepares the clients and environment.

    initial and other are paths to the binary used initially, and a binary
    used later.  base_env is the name of the environment to base the
    environment on and environment_name is the new name for the environment.
    """
    initial_client, other_client, teardown_client = get_clients(
        initial, other, base_env, debug, agent_url)
    jes_enabled = initial_client.is_jes_enabled()
    bs_manager = BootstrapManager(
        environment_name, initial_client, teardown_client,
        bootstrap_host=None, machines=[], series=series, agent_url=agent_url,
        agent_stream=agent_stream, region=None, log_dir=log_dir,
        keep_env=False, permanent=jes_enabled, jes_enabled=jes_enabled)
    test_control_heterogeneous(bs_manager, other_client, upload_tools)


@contextmanager
def run_context(bs_manager, other, upload_tools):
    try:
        bs_manager.keep_env = True
        with bs_manager.booted_context(upload_tools):
            if other.env.juju_home != bs_manager.client.env.juju_home:
                raise AssertionError('Juju home out of sync')
            yield
        # Test clean shutdown of an environment.
        callback_with_fallback(other, bs_manager.tear_down_client,
                               nice_tear_down)
    except:
        bs_manager.tear_down()
        raise


def test_control_heterogeneous(bs_manager, other, upload_tools):
    """Test if one binary can control an environment set up by the other."""
    initial = bs_manager.client
    released = bs_manager.tear_down_client
    with run_context(bs_manager, other, upload_tools):
        token = prepare_dummy_env(initial)
        initial.wait_for_started()
        if sys.platform != "win32":
            # Currently, juju ssh is not working on Windows.
            check_token(initial, token)
            check_series(other)
            other.juju('run', ('--all', 'uname -a'))
        other.get_config('dummy-source')
        other.get_model_config()
        other.juju('remove-relation', ('dummy-source', 'dummy-sink'))
        status = other.get_status()
        other.juju('unexpose', ('dummy-sink',))
        status = other.get_status()
        if status.status['services']['dummy-sink']['exposed']:
            raise AssertionError('dummy-sink is still exposed')
        status = other.get_status()
        charm_path = local_charm_path(
            charm='dummy-sink', juju_ver=other.version)
        juju_with_fallback(other, released, 'deploy',
                           (charm_path, 'sink2'))
        other.wait_for_started()
        other.juju('add-relation', ('dummy-source', 'sink2'))
        status = other.get_status()
        other.juju('expose', ('sink2',))
        status = other.get_status()
        if 'sink2' not in status.status['services']:
            raise AssertionError('Sink2 missing')
        other.remove_service('sink2')
        for ignored in until_timeout(30):
            status = other.get_status()
            if 'sink2' not in status.status['services']:
                break
        else:
            raise AssertionError('Sink2 not destroyed')
        other.juju('add-relation', ('dummy-source', 'dummy-sink'))
        status = other.get_status()
        relations = status.status['services']['dummy-sink']['relations']
        if not relations['source'] == ['dummy-source']:
            raise AssertionError('source is not dummy-source.')
        other.juju('expose', ('dummy-sink',))
        status = other.get_status()
        if not status.status['services']['dummy-sink']['exposed']:
            raise AssertionError('dummy-sink is not exposed')
        other.juju('add-unit', ('dummy-sink',))
        if not has_agent(other, 'dummy-sink/1'):
            raise AssertionError('dummy-sink/1 was not added.')
        other.juju('remove-unit', ('dummy-sink/1',))
        status = other.get_status()
        if has_agent(other, 'dummy-sink/1'):
            raise AssertionError('dummy-sink/1 was not removed.')
        container_type = other.preferred_container()
        other.juju('add-machine', (container_type,))
        status = other.get_status()
        container_machine, = set(k for k, v in status.agent_items() if
                                 k.endswith('/{}/0'.format(container_type)))
        container_holder = container_machine.split('/')[0]
        other.juju('remove-machine', (container_machine,))
        wait_until_removed(other, container_machine)
        other.juju('remove-machine', (container_holder,))
        wait_until_removed(other, container_holder)

# suppress nosetests
test_control_heterogeneous.__test__ = False


def juju_with_fallback(other, released, command, args, include_e=True):
{'fraction_non_alphanumeric': 0.10520280923032822, 'fraction_numerical': 0.003511537910276623, 'mean_word_length': 2.991704805491991, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5192932', 'n_tokens_mistral': 4148, 'n_tokens_neox': 4029, 'n_words': 951}