<commit_before># Copyright 2017 The Bazel Authors. All rights reserved.
#
# 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.

"""Genrule which provides Apple's Xcode environment."""

load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("//lib:apple_support.bzl", "apple_support")

def _compute_make_variables(
        genfiles_dir,
        label,
        resolved_srcs,
        files_to_build):
    resolved_srcs_list = resolved_srcs.to_list()
    files_to_build_list = files_to_build.to_list()
    variables = {
        "OUTS": " ".join([x.path for x in files_to_build_list]),
        "SRCS": " ".join([x.path for x in resolved_srcs_list]),
    }
    if len(resolved_srcs_list) == 1:
        variables["<"] = resolved_srcs_list[0].path
    if len(files_to_build_list) == 1:
        variables["@"] = files_to_build_list[0].path
        variables["@D"] = paths.dirname(variables["@"])
    else:
        variables["@D"] = genfiles_dir.path + "/" + label.package
    return variables

def _apple_genrule_impl(ctx):
    if not ctx.outputs.outs:
        fail("apple_genrule must have one or more outputs", attr = "outs")
    files_to_build = depset(ctx.outputs.outs)

    if ctx.attr.executable and len(files_to_build.to_list()) > 1:
        fail(
            "if genrules produce executables, they are allowed only one output. " +
            "If you need the executable=1 argument, then you should split this " +
            "genrule into genrules producing single outputs",
            attr = "executable",
        )

    resolved_srcs = depset(transitive = [dep.files for dep in ctx.attr.srcs])
    label_dict = {dep.label: dep.files.to_list() for dep in ctx.attr.srcs}

    resolved_inputs, argv, runfiles_manifests = ctx.resolve_command(
        command = ctx.attr.cmd,
        attribute = "cmd",
        expand_locations = True,
        make_variables = _compute_make_variables(
            ctx.genfiles_dir,
            ctx.label,
            depset(resolved_srcs.to_list()),
            files_to_build,
        ),
        tools = ctx.attr.tools,
        label_dict = label_dict,
        execution_requirements = apple_support.action_required_execution_requirements(ctx),
    )

    message = ctx.attr.message or "Executing apple_genrule"

    extra_args = {}
    if ctx.attr.no_sandbox:
        extra_args["execution_requirements"] = {"no-sandbox": "1"}

    apple_support.run_shell(
        ctx,
        inputs = resolved_srcs.to_list() + resolved_inputs,
        outputs = files_to_build.to_list(),
        env = ctx.configuration.default_shell_env,
        command = " ".join(argv),
        progress_message = "%s %s" % (message, ctx.label),
        mnemonic = "Genrule",
        input_manifests = runfiles_manifests,
        **extra_args
    )

    return [
        DefaultInfo(
            files = files_to_build,
            data_runfiles = ctx.runfiles(transitive_files = files_to_build),
        ),
    ]

_apple_genrule_inner = rule(
    implementation = _apple_genrule_impl,
    attrs = dicts.add(apple_support.action_required_attrs(), {
        "srcs": attr.label_list(allow_files = True),
        "outs": attr.output_list(mandatory = True),
        "cmd": attr.string(mandatory = True),
        "executable": attr.bool(default = False),
        "message": attr.string(),
        "no_sandbox": attr.bool(),
        "tools": attr.label_list(cfg = "host", allow_files = True),
    }),
    output_to_genfiles = True,
    fragments = ["apple"],
)

def apple_genrule(
        name,
        cmd,
        executable = False,
        outs = [],
        srcs = [],
        message = None,
        tools = [],
        no_sandbox = False,
        **kwargs):
    """Genrule which provides Apple specific environment and make variables.

    This mirrors the native genrule except that it provides a different set of
    make variables. This rule will only run on a Mac.

    Example of use:

    ```
    load("@build_bazel_apple_support//rules:apple_genrule.bzl", "apple_genrule")

    apple_genrule(
        name = "world",
        outs = ["hi"],
        cmd = "touch $@",
    )
    ```

    This rule also does location expansion, much like the native genrule.
    For example, `$(location hi)` may be used to refer to the output in the
    above example.

    The set of make variables that are supported for this rule:

    * `OUTS`: The outs list. If you have only one output file, you can also use
              `$@`.
    * `SRCS`: The srcs list (or more precisely, the pathnames of the files
              corresponding to labels in the srcs list). If you have only one
              source file, you can also use `$<`.
    * `<`: srcs, if it's a single file.
    * `@`: outs, if it's a single file.
    * `@D`: The output directory. If there is only one filename in outs, this
            expands to the directory containing that file. If there are
            multiple filenames, this variable instead expands to the package's
            root directory in the genfiles tree, even if all the generated
            files belong to the same subdirectory.

    The following environment variables are added to the rule action:

    * `DEVELOPER_DIR`: The base developer directory as defined on Apple
                       architectures, most commonly used in invoking Apple
                       tools such as xcrun.
    * `SDKROOT`: The base SDK directory as defined on Apple architectures, most
                 commonly used in invoking Apple tools such as xcrun.

    Args:
      name: The name of the target.
      cmd: The command to run. Subject the variable substitution.
      executable: Boolean. Declare output to be executable. Setting this flag to
        1 means the output is an executable file and can be run using the run
        command. The genrule must produce exactly one output in this case.
      outs: A list of files generated by this rule. If the executable flag is
        set, outs must contain exactly one label.
      srcs: A list of inputs for this rule, such as source files to process.
      message: A progress message to be reported as the rule runs.
      tools: A list of tool dependencies for this rule, they will be available
        when the action is run.
      no_sandbox: If the sandbox should be disabled when the action is run.
      **kwargs: Extra args meant to just be the common rules for all rules
        (tags, etc.).
    """
    if executable:
        if len(outs) != 1:
            fail("apple_genrule, if executable, must have exactly one output")

        intermediate_out = outs[0] + ".nonexecutable"

        # Remove any visibility and make this sub rule private since it is an
        # implementation detail.
        sub_kwargs = dict(kwargs)
        sub_kwargs.pop("visibility", None)
        _apple_genrule_inner(
            name = name + "_nonexecutable",
            outs = [intermediate_out],
            cmd = cmd,
            srcs = srcs,
            message = message,
            tools = tools,
            no_sandbox = no_sandbox,
            executable = executable,
            visibility = ["//visibility:private"],
            **sub_kwargs
        )

        # Remove anything from kwargs that might have a meaning that isn't wanted
        # on the genrule that does the copy. Generally, we are just trying to
        # keep things like testonly, visibility, etc.
        trimmed_kwargs = dict(kwargs)
        trimmed_kwargs.pop("stamp", None)
        native.genrule(
            name = name,
            outs = outs,
            srcs = [intermediate_out],
            cmd = "cp $< $@",
            executable = True,
            **trimmed_kwargs
        )
    else:
        _apple_genrule_inner(
            name = name,
            outs = outs,
            cmd = cmd,
            srcs = srcs,
            message = message,
            tools = tools,
            executable = executable,
            no_sandbox = no_sandbox,
            **kwargs
        )
<commit_msg>For `apple_genrule`, use the `exec` configuration for `tools` and apply the correct execution platform constraint to the rule.<commit_after># Copyright 2017 The Bazel Authors. All rights reserved.
#
# 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.

"""Genrule which provides Apple's Xcode environment."""

load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("//lib:apple_support.bzl", "apple_support")

def _compute_make_variables(
        genfiles_dir,
        label,
        resolved_srcs,
        files_to_build):
    resolved_srcs_list = resolved_srcs.to_list()
    files_to_build_list = files_to_build.to_list()
    variables = {
        "OUTS": " ".join([x.path for x in files_to_build_list]),
        "SRCS": " ".join([x.path for x in resolved_srcs_list]),
    }
    if len(resolved_srcs_list) == 1:
        variables["<"] = resolved_srcs_list[0].path
    if len(files_to_build_list) == 1:
        variables["@"] = files_to_build_list[0].path
        variables["@D"] = paths.dirname(variables["@"])
    else:
        variables["@D"] = genfiles_dir.path + "/" + label.package
    return variables

def _apple_genrule_impl(ctx):
    if not ctx.outputs.outs:
        fail("apple_genrule must have one or more outputs", attr = "outs")
    files_to_build = depset(ctx.outputs.outs)

    if ctx.attr.executable and len(files_to_build.to_list()) > 1:
        fail(
            "if genrules produce executables, they are allowed only one output. " +
            "If you need the executable=1 argument, then you should split this " +
            "genrule into genrules producing single outputs",
            attr = "executable",
        )

    resolved_srcs = depset(transitive = [dep.files for dep in ctx.attr.srcs])
    label_dict = {dep.label: dep.files.to_list() for dep in ctx.attr.srcs}

    resolved_inputs, argv, runfiles_manifests = ctx.resolve_command(
        command = ctx.attr.cmd,
        attribute = "cmd",
        expand_locations = True,
        make_variables = _compute_make_variables(
            ctx.genfiles_dir,
            ctx.label,
            depset(resolved_srcs.to_list()),
            files_to_build,
        ),
        tools = ctx.attr.tools,
        label_dict = label_dict,
        execution_requirements = apple_support.action_required_execution_requirements(ctx),
    )

    message = ctx.attr.message or "Executing apple_genrule"

    extra_args = {}
    if ctx.attr.no_sandbox:
        extra_args["execution_requirements"] = {"no-sandbox": "1"}

    apple_support.run_shell(
        ctx,
        inputs = resolved_srcs.to_list() + resolved_inputs,
        outputs = files_to_build.to_list(),
        env = ctx.configuration.default_shell_env,
        command = " ".join(argv),
        progress_message = "%s %s" % (message, ctx.label),
        mnemonic = "Genrule",
        input_manifests = runfiles_manifests,
        **extra_args
    )

    return [
        DefaultInfo(
            files = files_to_build,
            data_runfiles = ctx.runfiles(transitive_files = files_to_build),
        ),
    ]

_apple_genrule_inner = rule(
    implementation = _apple_genrule_impl,
    attrs = dicts.add(apple_support.action_required_attrs(), {
        "srcs": attr.label_list(allow_files = True),
        "outs": attr.output_list(mandatory = True),
        "cmd": attr.string(mandatory = True),
        "executable": attr.bool(default = False),
        "message": attr.string(),
        "no_sandbox": attr.bool(),
        "tools": attr.label_list(cfg = "exec", allow_files = True),
    }),
    exec_compatible_with = ["@platforms//os:macos"],
    output_to_genfiles = True,
    fragments = ["apple"],
)

def apple_genrule(
        name,
        cmd,
        executable = False,
        outs = [],
        srcs = [],
        message = None,
        tools = [],
        no_sandbox = False,
        **kwargs):
    """Genrule which provides Apple specific environment and make variables.

    This mirrors the native genrule except that it provides a different set of
    make variables. This rule will only run on a Mac.

    Example of use:

    ```
    load("@build_bazel_apple_support//rules:apple_genrule.bzl", "apple_genrule")

    apple_genrule(
        name = "world",
        outs = ["hi"],
        cmd = "touch $@",
    )
    ```

    This rule also does location expansion, much like the native genrule.
    For example, `$(location hi)` may be used to refer to the output in the
    above example.

    The set of make variables that are supported for this rule:

    * `OUTS`: The outs list. If you have only one output file, you can also use
              `$@`.
    * `SRCS`: The srcs list (or more precisely, the pathnames of the files
              corresponding to labels in the srcs list). If you have only one
              source file, you can also use `$<`.
    * `<`: srcs, if it's a single file.
    * `@`: outs, if it's a single file.
    * `@D`: The output directory. If there is only one filename in outs, this
            expands to the directory containing that file. If there are
            multiple filenames, this variable instead expands to the package's
            root directory in the genfiles tree, even if all the generated
            files belong to the same subdirectory.

    The following environment variables are added to the rule action:

    * `DEVELOPER_DIR`: The base developer directory as defined on Apple
                       architectures, most commonly used in invoking Apple
                       tools such as xcrun.
    * `SDKROOT`: The base SDK directory as defined on Apple architectures, most
                 commonly used in invoking Apple tools such as xcrun.

    Args:
      name: The name of the target.
      cmd: The command to run. Subject the variable substitution.
      executable: Boolean. Declare output to be executable. Setting this flag to
        1 means the output is an executable file and can be run using the run
        command. The genrule must produce exactly one output in this case.
      outs: A list of files generated by this rule. If the executable flag is
        set, outs must contain exactly one label.
      srcs: A list of inputs for this rule, such as source files to process.
      message: A progress message to be reported as the rule runs.
      tools: A list of tool dependencies for this rule, they will be available
        when the action is run.
      no_sandbox: If the sandbox should be disabled when the action is run.
      **kwargs: Extra args meant to just be the common rules for all rules
        (tags, etc.).
    """
    if executable:
        if len(outs) != 1:
            fail("apple_genrule, if executable, must have exactly one output")

        intermediate_out = outs[0] + ".nonexecutable"

        # Remove any visibility and make this sub rule private since it is an
        # implementation detail.
        sub_kwargs = dict(kwargs)
        sub_kwargs.pop("visibility", None)
        _apple_genrule_inner(
            name = name + "_nonexecutable",
            outs = [intermediate_out],
            cmd = cmd,
            srcs = srcs,
            message = message,
            tools = tools,
            no_sandbox = no_sandbox,
            executable = executable,
            visibility = ["//visibility:private"],
            **sub_kwargs
        )

        # Remove anything from kwargs that might have a meaning that isn't wanted
        # on the genrule that does the copy. Generally, we are just trying to
        # keep things like testonly, visibility, etc.
        trimmed_kwargs = dict(kwargs)
        trimmed_kwargs.pop("stamp", None)
        native.genrule(
            name = name,
            outs = outs,
            srcs = [intermediate_out],
            cmd = "cp $< $@",
            executable = True,
            **trimmed_kwargs
        )
    else:
        _apple_genrule_inner(
            name = name,
            outs = outs,
            cmd = cmd,
            srcs = srcs,
            message = message,
            tools = tools,
            executable = executable,
            no_sandbox = no_sandbox,
            **kwargs
        )
<|endoftext|>


<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2013 dotCloud, Inc.
# All Rights Reserved.
#
#    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.

"""
A Docker Hypervisor which allows running Linux Containers instead of VMs.
"""

import base64
import os
import random
import socket
import time

from oslo.config import cfg

from nova.compute import power_state
from nova import exception
from nova.openstack.common import log
from nova import utils
from nova.virt.docker import client
from nova.virt.docker import hostinfo
from nova.virt import driver

CONF = cfg.CONF
CONF.import_opt('host', 'nova.netconf')

LOG = log.getLogger(__name__)

class DockerDriver(driver.ComputeDriver):
    capabilities = {
        'has_imagecache': True,
        'supports_recreate': True,
    }

    """Docker hypervisor driver."""

    def __init__(self, virtapi, read_only=False):
        super(DockerDriver, self).__init__(virtapi)
        self.docker = client.DockerHTTPClient()
        self.virtapi = virtapi
        self.fake = False

    def use_mock_client(self):
        """Replace HTTP Client with the Mock one (useful for unit tests)."""
        self.docker = client.MockClient()
        self.fake = True

    def init_host(self, host):
        if self.docker.is_daemon_running() is False:
            raise exception.NovaException("Docker daemon is not running")

    def list_instances(self, _inspect=False):
        res = []
        for container in self.docker.list_containers():
            info = self.docker.inspect_container(container['id'])
            if _inspect:
                res.append(info)
            else:
                res.append(info['Config'].get('Hostname'))
        return res

    def legacy_nwinfo(self):
        return True

    def plug_vifs(self, instance, network_info):
        """Plug VIFs into networks."""
        pass

    def unplug_vifs(self, instance, network_info):
        """Unplug VIFs from networks."""
        pass

    def find_container_by_name(self, name):
        for info in self.list_instances(_inspect=True):
            if info['Config'].get('Hostname') == name:
                return info
        return {}

    def get_info(self, instance):
        container = self.find_container_by_name(instance['name'])
        if not container:
            raise exception.InstanceNotFound(instance_id=instance['name'])
        running = container['State'].get('Running')
        info = {
            'max_mem': 0,
            'mem': 0,
            'num_cpu': 1,
            'cpu_time': 0
        }
        info['state'] = power_state.RUNNING if running \
            else power_state.SHUTDOWN
        return info

    def get_host_stats(self, refresh=False):
        hostname = socket.gethostname()
        memory = hostinfo.get_memory_usage()
        disk = hostinfo.get_disk_usage()
        stats = {
            'vcpus': 1,
            'hypervisor_hostname': hostname,
            'host_hostname': hostname,
            'host_name_label': hostname,
            'host_name-description': hostname,
            'host_memory_total': memory['total'],
            'host_memory_overhead': memory['used'],
            'host_memory_free': memory['free'],
            'host_memory_free_computed': memory['free'],
            'host_other_config': {},
            'host_cpu_info': {},
            'disk_available': disk['available'],
            'disk_total': disk['total'],
            'disk_used': disk['used']
        }
        return stats

    def get_available_resource(self, nodename):
        memory = hostinfo.get_memory_usage()
        disk = hostinfo.get_disk_usage()
        stats = {
            'vcpus': 1,
            'vcpus_used': 0,
            'memory_mb': memory['total'] / (1024 ** 2),
            'local_gb': disk['total'] / (1024 ** 3),
            'memory_mb_used': memory['used'] / (1024 ** 2),
            'local_gb_used': disk['used'] / (1024 ** 3),
            'available_least': disk['available'] / (1024 ** 3),
            'hypervisor_type': 'docker',
            'hypervisor_version': '1.0',
            'hypervisor_hostname': nodename,
            'cpu_info': '?'
        }
        return stats

    def _find_cgroup_devices_path(self):
        for ln in open('/proc/mounts'):
            if ln.startswith('cgroup ') and 'devices' in ln:
                return ln.split(' ')[1]

    def _find_container_pid(self, container_id):
        cgroup_path = self._find_cgroup_devices_path()
        lxc_path = os.path.join(cgroup_path, 'lxc')
        tasks_path = os.path.join(lxc_path, container_id, 'tasks')
        n = 0
        while True:
            if n > 20:
                return
            try:
                with open(tasks_path) as f:
                    pids = f.readlines()
                    if pids:
                        return int(pids[0].strip())
            except IOError:
                pass
            time.sleep(0.5)
            n += 1

    def _setup_network(self, instance, network_info):
        if self.fake is True or not network_info:
            return
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        network_info = network_info[0]
        netns_path = '/var/run/netns'
        if not os.path.exists(netns_path):
            utils.execute(
                'mkdir', '-p', netns_path, run_as_root=True)
        nspid = self._find_container_pid(container_id)
        if not nspid:
            raise RuntimeError(
                'Cannot find any PID under '
                'container "{0}"'.format(container_id))
        netns_path = os.path.join(netns_path, container_id)
        utils.execute(
            'ln', '-sf', '/proc/{0}/ns/net'.format(nspid),
            '/var/run/netns/{0}'.format(container_id),
            run_as_root=True)
        rand = random.randint(0, 100000)
        if_local_name = 'pvnetl{0}'.format(rand)
        if_remote_name = 'pvnetr{0}'.format(rand)
        bridge = network_info[0]['bridge']
        ip = network_info[1]['ips'][0]['ip']
        utils.execute(
            'ip', 'link', 'add', 'name', if_local_name, 'type',
            'veth', 'peer', 'name', if_remote_name,
            run_as_root=True)
        utils.execute(
            'brctl', 'addif', bridge, if_local_name,
            run_as_root=True)
        utils.execute(
            'ip', 'link', 'set', if_local_name, 'up',
            run_as_root=True)
        utils.execute(
            'ip', 'link', 'set', if_remote_name, 'netns', nspid,
            run_as_root=True)
        utils.execute(
            'ip', 'netns', 'exec', container_id, 'ifconfig',
            if_remote_name, ip,
            run_as_root=True)

    def _parse_user_data(self, user_data):
        data = {}
        user_data = base64.b64decode(user_data)
        for ln in user_data.split('\n'):
            ln = ln.strip()
            if not ln or ':' not in ln:
                continue
            if ln.startswith('#'):
                continue
            ln = ln.split(':', 1)
            data[ln[0].strip()] = ln[1].strip('"\' ')
        return data

    def _get_memory_limit_bytes(self, instance):
        for metadata in instance.get('system_metadata', []):
            if not metadata['deleted'] and \
                    metadata['key'] == 'instance_type_memory_mb':
                        return int(metadata['value']) * 1024 * 1024
        return 0

    def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info=None, block_device_info=None):
        cmd = ['/bin/sh']
        user_data = instance.get('user_data')
        image_name = 'ubuntu'
        if user_data:
            user_data = self._parse_user_data(user_data)
            if 'cmd' in user_data:
                cmd = ['/bin/sh', '-c', user_data.get('cmd')]
            if 'image' in user_data:
                image_name = user_data.get('image')
        args = {
            'Hostname': instance['name'],
            'Image': image_name,
            'Cmd': cmd,
            'Memory': self._get_memory_limit_bytes(instance)
        }
        container_id = self.docker.create_container(args)
        if container_id is None:
            LOG.info('Image name "{0}" does not exist, fetching it...'.format(
                image_name))
            res = self.docker.pull_repository(image_name)
            if res is False:
                raise exception.InstanceDeployFailure(
                    'Cannot pull missing image',
                    instance_id=instance['name'])
            container_id = self.docker.create_container(args)
            if container_id is None:
                raise exception.InstanceDeployFailure(
                    'Cannot create container',
                    instance_id=instance['name'])
        self.docker.start_container(container_id)
        try:
            self._setup_network(instance, network_info)
        except Exception as e:
            raise exception.InstanceDeployFailure(
                'Cannot setup network: {0}'.format(e),
                instance_id=instance['name'])

    def destroy(self, instance, network_info, block_device_info=None,
                destroy_disks=True):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)
        self.docker.destroy_container(container_id)

    def reboot(self, context, instance, network_info, reboot_type,
               block_device_info=None, bad_volumes_callback=None):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)
        self.docker.start_container(container_id)

    def power_on(self, context, instance, network_info, block_device_info):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.start_container(container_id)

    def power_off(self, instance):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)

    def get_console_output(self, instance):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        return self.docker.get_container_logs(container_id)
<commit_msg>Improved error message<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2013 dotCloud, Inc.
# All Rights Reserved.
#
#    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.

"""
A Docker Hypervisor which allows running Linux Containers instead of VMs.
"""

import base64
import os
import random
import socket
import time

from oslo.config import cfg

from nova.compute import power_state
from nova import exception
from nova.openstack.common import log
from nova import utils
from nova.virt.docker import client
from nova.virt.docker import hostinfo
from nova.virt import driver

CONF = cfg.CONF
CONF.import_opt('host', 'nova.netconf')

LOG = log.getLogger(__name__)

class DockerDriver(driver.ComputeDriver):
    capabilities = {
        'has_imagecache': True,
        'supports_recreate': True,
    }

    """Docker hypervisor driver."""

    def __init__(self, virtapi, read_only=False):
        super(DockerDriver, self).__init__(virtapi)
        self.docker = client.DockerHTTPClient()
        self.virtapi = virtapi
        self.fake = False

    def use_mock_client(self):
        """Replace HTTP Client with the Mock one (useful for unit tests)."""
        self.docker = client.MockClient()
        self.fake = True

    def init_host(self, host):
        if self.docker.is_daemon_running() is False:
            raise exception.NovaException("Docker daemon is not running or is "
                    "not reachable (check the rights on /var/run/docker.sock)")

    def list_instances(self, _inspect=False):
        res = []
        for container in self.docker.list_containers():
            info = self.docker.inspect_container(container['id'])
            if _inspect:
                res.append(info)
            else:
                res.append(info['Config'].get('Hostname'))
        return res

    def legacy_nwinfo(self):
        return True

    def plug_vifs(self, instance, network_info):
        """Plug VIFs into networks."""
        pass

    def unplug_vifs(self, instance, network_info):
        """Unplug VIFs from networks."""
        pass

    def find_container_by_name(self, name):
        for info in self.list_instances(_inspect=True):
            if info['Config'].get('Hostname') == name:
                return info
        return {}

    def get_info(self, instance):
        container = self.find_container_by_name(instance['name'])
        if not container:
            raise exception.InstanceNotFound(instance_id=instance['name'])
        running = container['State'].get('Running')
        info = {
            'max_mem': 0,
            'mem': 0,
            'num_cpu': 1,
            'cpu_time': 0
        }
        info['state'] = power_state.RUNNING if running \
            else power_state.SHUTDOWN
        return info

    def get_host_stats(self, refresh=False):
        hostname = socket.gethostname()
        memory = hostinfo.get_memory_usage()
        disk = hostinfo.get_disk_usage()
        stats = {
            'vcpus': 1,
            'hypervisor_hostname': hostname,
            'host_hostname': hostname,
            'host_name_label': hostname,
            'host_name-description': hostname,
            'host_memory_total': memory['total'],
            'host_memory_overhead': memory['used'],
            'host_memory_free': memory['free'],
            'host_memory_free_computed': memory['free'],
            'host_other_config': {},
            'host_cpu_info': {},
            'disk_available': disk['available'],
            'disk_total': disk['total'],
            'disk_used': disk['used']
        }
        return stats

    def get_available_resource(self, nodename):
        memory = hostinfo.get_memory_usage()
        disk = hostinfo.get_disk_usage()
        stats = {
            'vcpus': 1,
            'vcpus_used': 0,
            'memory_mb': memory['total'] / (1024 ** 2),
            'local_gb': disk['total'] / (1024 ** 3),
            'memory_mb_used': memory['used'] / (1024 ** 2),
            'local_gb_used': disk['used'] / (1024 ** 3),
            'available_least': disk['available'] / (1024 ** 3),
            'hypervisor_type': 'docker',
            'hypervisor_version': '1.0',
            'hypervisor_hostname': nodename,
            'cpu_info': '?'
        }
        return stats

    def _find_cgroup_devices_path(self):
        for ln in open('/proc/mounts'):
            if ln.startswith('cgroup ') and 'devices' in ln:
                return ln.split(' ')[1]

    def _find_container_pid(self, container_id):
        cgroup_path = self._find_cgroup_devices_path()
        lxc_path = os.path.join(cgroup_path, 'lxc')
        tasks_path = os.path.join(lxc_path, container_id, 'tasks')
        n = 0
        while True:
            if n > 20:
                return
            try:
                with open(tasks_path) as f:
                    pids = f.readlines()
                    if pids:
                        return int(pids[0].strip())
            except IOError:
                pass
            time.sleep(0.5)
            n += 1

    def _setup_network(self, instance, network_info):
        if self.fake is True or not network_info:
            return
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        network_info = network_info[0]
        netns_path = '/var/run/netns'
        if not os.path.exists(netns_path):
            utils.execute(
                'mkdir', '-p', netns_path, run_as_root=True)
        nspid = self._find_container_pid(container_id)
        if not nspid:
            raise RuntimeError(
                'Cannot find any PID under '
                'container "{0}"'.format(container_id))
        netns_path = os.path.join(netns_path, container_id)
        utils.execute(
            'ln', '-sf', '/proc/{0}/ns/net'.format(nspid),
            '/var/run/netns/{0}'.format(container_id),
            run_as_root=True)
        rand = random.randint(0, 100000)
        if_local_name = 'pvnetl{0}'.format(rand)
        if_remote_name = 'pvnetr{0}'.format(rand)
        bridge = network_info[0]['bridge']
        ip = network_info[1]['ips'][0]['ip']
        utils.execute(
            'ip', 'link', 'add', 'name', if_local_name, 'type',
            'veth', 'peer', 'name', if_remote_name,
            run_as_root=True)
        utils.execute(
            'brctl', 'addif', bridge, if_local_name,
            run_as_root=True)
        utils.execute(
            'ip', 'link', 'set', if_local_name, 'up',
            run_as_root=True)
        utils.execute(
            'ip', 'link', 'set', if_remote_name, 'netns', nspid,
            run_as_root=True)
        utils.execute(
            'ip', 'netns', 'exec', container_id, 'ifconfig',
            if_remote_name, ip,
            run_as_root=True)

    def _parse_user_data(self, user_data):
        data = {}
        user_data = base64.b64decode(user_data)
        for ln in user_data.split('\n'):
            ln = ln.strip()
            if not ln or ':' not in ln:
                continue
            if ln.startswith('#'):
                continue
            ln = ln.split(':', 1)
            data[ln[0].strip()] = ln[1].strip('"\' ')
        return data

    def _get_memory_limit_bytes(self, instance):
        for metadata in instance.get('system_metadata', []):
            if not metadata['deleted'] and \
                    metadata['key'] == 'instance_type_memory_mb':
                        return int(metadata['value']) * 1024 * 1024
        return 0

    def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info=None, block_device_info=None):
        cmd = ['/bin/sh']
        user_data = instance.get('user_data')
        image_name = 'ubuntu'
        if user_data:
            user_data = self._parse_user_data(user_data)
            if 'cmd' in user_data:
                cmd = ['/bin/sh', '-c', user_data.get('cmd')]
            if 'image' in user_data:
                image_name = user_data.get('image')
        args = {
            'Hostname': instance['name'],
            'Image': image_name,
            'Cmd': cmd,
            'Memory': self._get_memory_limit_bytes(instance)
        }
        container_id = self.docker.create_container(args)
        if container_id is None:
            LOG.info('Image name "{0}" does not exist, fetching it...'.format(
                image_name))
            res = self.docker.pull_repository(image_name)
            if res is False:
                raise exception.InstanceDeployFailure(
                    'Cannot pull missing image',
                    instance_id=instance['name'])
            container_id = self.docker.create_container(args)
            if container_id is None:
                raise exception.InstanceDeployFailure(
                    'Cannot create container',
                    instance_id=instance['name'])
        self.docker.start_container(container_id)
        try:
            self._setup_network(instance, network_info)
        except Exception as e:
            raise exception.InstanceDeployFailure(
                'Cannot setup network: {0}'.format(e),
                instance_id=instance['name'])

    def destroy(self, instance, network_info, block_device_info=None,
                destroy_disks=True):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)
        self.docker.destroy_container(container_id)

    def reboot(self, context, instance, network_info, reboot_type,
               block_device_info=None, bad_volumes_callback=None):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)
        self.docker.start_container(container_id)

    def power_on(self, context, instance, network_info, block_device_info):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.start_container(container_id)

    def power_off(self, instance):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        self.docker.stop_container(container_id)

    def get_console_output(self, instance):
        container_id = self.find_container_by_name(instance['name']).get('id')
        if not container_id:
            return
        return self.docker.get_container_logs(container_id)
<|endoftext|>


<commit_before>__version__ = '3.0'
<commit_msg>Version bump<commit_after>__version__ = '3.0.1'
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('../'))

# -- Project information -----------------------------------------------------

project = 'Python library for running IGF pipelines'
copyright = '2018, Avik Datta, IGF Team'
author = 'Avik Datta, IGF Team'

# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = '1.4'

# -- General configuration ---------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.githubpages',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'bizstyle'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself.  Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}

# -- Options for HTMLHelp output ---------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'PythonlibraryforrunningIGFpipelinesdoc'

# -- Options for LaTeX output ------------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'PythonlibraryforrunningIGFpipelines.tex', 'Python library for running IGF pipelines Documentation',
     'Avik Datta, IGF Team', 'manual'),
]

# -- Options for manual page output ------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'pythonlibraryforrunningigfpipelines', 'Python library for running IGF pipelines Documentation',
     [author], 1)
]

# -- Options for Texinfo output ----------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'PythonlibraryforrunningIGFpipelines', 'Python library for running IGF pipelines Documentation',
     author, 'PythonlibraryforrunningIGFpipelines', 'One line description of project.',
     'Miscellaneous'),
]

# -- Extension configuration -------------------------------------------------
<commit_msg>updated sphinx conf<commit_after># -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('../'))

# -- Project information -----------------------------------------------------

project = 'Python library for running IGF pipelines'
copyright = '2018, Avik Datta, IGF Team'
author = 'Avik Datta, IGF Team'

# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = '1.4'

# -- General configuration ---------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.githubpages',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'nature'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself.  Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}

# -- Options for HTMLHelp output ---------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'PythonlibraryforrunningIGFpipelinesdoc'

# -- Options for LaTeX output ------------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
     'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
     'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'PythonlibraryforrunningIGFpipelines.tex', 'Python library for running IGF pipelines Documentation',
     'Avik Datta, IGF Team', 'manual'),
]

# -- Options for manual page output ------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'pythonlibraryforrunningigfpipelines', 'Python library for running IGF pipelines Documentation',
     [author], 1)
]

# -- Options for Texinfo output ----------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'PythonlibraryforrunningIGFpipelines', 'Python library for running IGF pipelines Documentation',
     author, 'PythonlibraryforrunningIGFpipelines', 'One line description of project.',
     'Miscellaneous'),
]

# -- Extension configuration -------------------------------------------------
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from openerp.osv import orm, fields, osv
from openerp.addons.t4activity.activity import except_if
import logging
_logger = logging.getLogger(__name__)

class t4_clinical_o2level(orm.Model):
    _name = 't4.clinical.o2level'
    _columns = {
        'name': fields.integer("O2 Target Name"),
        'min': fields.integer("Min"),
        'max': fields.integer("Max"),
    }

class t4_clinical_patient_o2target(orm.Model):
    _name = 't4.clinical.patient.o2target'
    _inherit = ['t4.activity.data']
    _rec_name = 'level_id'
    _columns = {
        'level_id': fields.many2one('t4.clinical.o2level', 'O2 Level', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }

class t4_clinical_patient_mrsa(orm.Model):
    _name = 't4.clinical.patient.mrsa'
    _inherit = ['t4.activity.data']
    _columns = {
        'mrsa': fields.boolean('MRSA', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }

class t4_clinical_patient_diabetes(orm.Model):
    _name = 't4.clinical.patient.diabetes'
    _inherit = ['t4.activity.data']
    _columns = {
        'diabetes': fields.boolean('Diabetes', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }     <commit_msg>[FIX] O2 level name<commit_after># -*- coding: utf-8 -*-

from openerp.osv import orm, fields, osv
from openerp.addons.t4activity.activity import except_if
import logging
_logger = logging.getLogger(__name__)

class t4_clinical_o2level(orm.Model):
    _name = 't4.clinical.o2level'
    _columns = {
        'name': fields.char("O2 Target Name", size=5),
        'min': fields.integer("Min"),
        'max': fields.integer("Max"),
    }

class t4_clinical_patient_o2target(orm.Model):
    _name = 't4.clinical.patient.o2target'
    _inherit = ['t4.activity.data']
    _rec_name = 'level_id'
    _columns = {
        'level_id': fields.many2one('t4.clinical.o2level', 'O2 Level', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }

class t4_clinical_patient_mrsa(orm.Model):
    _name = 't4.clinical.patient.mrsa'
    _inherit = ['t4.activity.data']
    _columns = {
        'mrsa': fields.boolean('MRSA', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }

class t4_clinical_patient_diabetes(orm.Model):
    _name = 't4.clinical.patient.diabetes'
    _inherit = ['t4.activity.data']
    _columns = {
        'diabetes': fields.boolean('Diabetes', required=True),
        'patient_id': fields.many2one('t4.clinical.patient', 'Patient', required=True),
   }     <|endoftext|>


<commit_before>#-*- coding: utf-8
"""
collects common base functionality
"""
import doc
import posixpath
import terminology

class baseobject(object):
    _format = None

    def get_terminology_equivalent(self):
        return None

    def __eq__(self, obj):
        """
        do a deep comparison of this object and its odml properties
        """
        # cannot compare totally different stuff
        if not isinstance(obj, baseobject): return False
        if not isinstance(self, obj.__class__ ): return False

        for key in self._format:
            if getattr(self, key) != getattr(obj, key):
                return False

        return True

    def __ne__(self, obj):
        """
        use the __eq__ function to determine if both objects are equal
        """
        return not self == obj

    def clean(self):
        pass

    def clone(self, children=True):
        """
        clone this object recursively (if children is True) allowing to copy it independently
        to another document. If children is False, this acts as a template cloner, creating
        a copy of the object without any children
        """
        # TODO don't we need some recursion / deepcopy here?
        import copy
        obj = copy.copy(self)
        return obj

class SafeList(list):
    def remove(self, obj):
        """
        remove an element from this list

        be sure to use "is" based comparison (instead of __cmp__ / ==)
        """
        for i, e in enumerate(self):
            if e is obj:
                del self[i]
                return
        raise ValueError("remove: %s not in list" % repr(obj))

class SmartList(SafeList):
    def __getitem__(self, key):
        """
        provides element index also by searching for an element with a given name
        """
        # try normal list index first (for integers)
        if type(key) is int:
            return super(SmartList, self).__getitem__(key)

        # otherwise search the list
        for obj in self:
            if (hasattr(obj, "name") and obj.name == key) or key == obj:
                return obj

        # and fail eventually
        raise KeyError(key)

class sectionable(baseobject):
    def __init__(self):
        self._sections = SmartList()
        self._repository = None

    @property
    def document(self):
        """
        returns the parent-most node (if its a document instance) or None
        """
        p = self
        while p.parent:
            p = p.parent
        if isinstance(p, doc.Document):
            return p

    @property
    def sections(self):
        return self._sections

    def append(self, section):
        """adds the section to the section-list and makes this document the section’s parent"""
        self._sections.append(section)
        section._parent = self

    def remove(self, section):
        self._sections.remove(section)
        section._parent = None

    def __getitem__(self, key):
        return self._sections[key]

    def __len__(self):
        return len(self._sections)

    def __iter__(self):
        return self._sections.__iter__()

    def itersections(self, recursive=False, yield_self=False):
        """
        iterate each child section

        if *recursive* is set, iterate all child sections recurively (depth-search)
        """
        if yield_self:
            yield self
        for i in self._sections:
            yield i
            if recursive:
                for j in i.itersections(recursive=recursive):
                    yield j

    def contains(self, obj):
        """
        checks if a subsection of name&type of *obj* is a child of this section
        if so return this child
        """
        for i in self._sections:
            if obj.name == i.name and obj.type == i.type:
                return i

    def _matches(self, obj, key=None, type=None):
        return (key is None or (key is not None and hasattr(obj, "name") and obj.name == key)) \
            and (type is None or (type is not None and hasattr(obj, "type") and obj.type == type))

    def find_by_path(self, path):
        """
        find a Section/Property through a path like "name1/name2"
        """
        path = path.split("/")
        return self._find_by_path(path)

    def _find_by_path(self, path):
        """
        find a Section/Property through a path like ("name1", "name2")
        """
        cur = self
        for i in path:
            if i == "." or i == "": continue
            if i == "..":
                cur = cur.parent
                continue
            cur = cur[i]
        return cur

    def find(self, key=None, type=None):
        """return the first subsection named *key* of type *type*"""
        for s in self._sections:
            if self._matches(s, key, type): return s

    def find_related(self, key=None, type=None, children=True, siblings=True, parents=True, recursive=True, findAll=False):
        """
        finds a related section named *key* and/or *type*

          * by searching its children’s children if *children* is True
            if *recursive* is true all leave nodes will be searched
          * by searching its siblings if *siblings* is True
          * by searching the parent element if *parents* is True
            if *recursive* is True all parent nodes until the root are searched
          * if *findAll* is True, returns a list of all matching objects
        """
        ret = []

        if children:
            for section in self._sections:
                if self._matches(section, key, type):
                    return section

                if recursive:
                    obj = section.find_related(key, type, children, siblings=False, parents=False, recursive=recursive, findAll=findAll)
                if obj is not None:
                    if findAll:
                        ret += obj
                    else:
                        return obj

        if siblings and self.parent is not None:
            obj = self.parent.find(key, type)
            if obj is not None:
                if findAll:
                    ret.append(obj)
                else:
                    return obj

        if parents:
            obj = self
            while obj.parent is not None:
                obj = obj.parent
                if self._matches(obj, key, type):
                    if findAll:
                        ret.append(obj)
                    else:
                        return obj
                if not recursive: break

        if ret:
            return ret
        return None

    def get_path(self):
        """
        returns the absolute path of this section
        """
        node = self
        path = []
        while node.parent is not None:
            path.insert(0, node.name)
            node = node.parent
        return "/" + "/".join(path)

    @staticmethod
    def _get_relative_path(a, b):
        """
        returns a relative path for navigation from dir *a* to dir *b*

        if the common parent of both is "/", return an absolute path
        """
        a += "/"
        b += "/"
        parent = posixpath.dirname(posixpath.commonprefix([a,b]))
        if parent == "/": return b[:-1]

        a = posixpath.relpath(a, parent)
        b = posixpath.relpath(b, parent)
        if a == ".": return b

        return posixpath.normpath("../" * (a.count("/")+1) + b)

    def get_relative_path(self, section):
        """
        returns a relative (file)path to point to section (e.g. ../other_section)

        if the common parent of both sections is the document (i.e. /), return an absolute path
        """
        a = self.get_path()
        b = section.get_path()
        return self._get_relative_path(a,b)

    def clean(self):
        for i in self:
            i.clean()

    def clone(self, children=True):
        """
        clone this object recursively allowing to copy it independently
        to another document
        """
        obj = super(sectionable, self).clone(children)
        obj._parent = None
        obj._sections = SmartList()
        if children:
            for s in self._sections:
                obj.append(s.clone())

        return obj

    @property
    def repository(self):
        return self._repository

    @repository.setter
    def repository(self, url):
        if not url: url = None
        self._repository = url
        if url:
            terminology.deferred_load(url)

    def get_repository(self):
        return self._repository
<commit_msg>base.find_related: findAll fixes<commit_after>#-*- coding: utf-8
"""
collects common base functionality
"""
import doc
import posixpath
import terminology

class baseobject(object):
    _format = None

    def get_terminology_equivalent(self):
        return None

    def __eq__(self, obj):
        """
        do a deep comparison of this object and its odml properties
        """
        # cannot compare totally different stuff
        if not isinstance(obj, baseobject): return False
        if not isinstance(self, obj.__class__ ): return False

        for key in self._format:
            if getattr(self, key) != getattr(obj, key):
                return False

        return True

    def __ne__(self, obj):
        """
        use the __eq__ function to determine if both objects are equal
        """
        return not self == obj

    def clean(self):
        pass

    def clone(self, children=True):
        """
        clone this object recursively (if children is True) allowing to copy it independently
        to another document. If children is False, this acts as a template cloner, creating
        a copy of the object without any children
        """
        # TODO don't we need some recursion / deepcopy here?
        import copy
        obj = copy.copy(self)
        return obj

class SafeList(list):
    def remove(self, obj):
        """
        remove an element from this list

        be sure to use "is" based comparison (instead of __cmp__ / ==)
        """
        for i, e in enumerate(self):
            if e is obj:
                del self[i]
                return
        raise ValueError("remove: %s not in list" % repr(obj))

class SmartList(SafeList):
    def __getitem__(self, key):
        """
        provides element index also by searching for an element with a given name
        """
        # try normal list index first (for integers)
        if type(key) is int:
            return super(SmartList, self).__getitem__(key)

        # otherwise search the list
        for obj in self:
            if (hasattr(obj, "name") and obj.name == key) or key == obj:
                return obj

        # and fail eventually
        raise KeyError(key)

class sectionable(baseobject):
    def __init__(self):
        self._sections = SmartList()
        self._repository = None

    @property
    def document(self):
        """
        returns the parent-most node (if its a document instance) or None
        """
        p = self
        while p.parent:
            p = p.parent
        if isinstance(p, doc.Document):
            return p

    @property
    def sections(self):
        return self._sections

    def append(self, section):
        """adds the section to the section-list and makes this document the section’s parent"""
        self._sections.append(section)
        section._parent = self

    def remove(self, section):
        self._sections.remove(section)
        section._parent = None

    def __getitem__(self, key):
        return self._sections[key]

    def __len__(self):
        return len(self._sections)

    def __iter__(self):
        return self._sections.__iter__()

    def itersections(self, recursive=False, yield_self=False):
        """
        iterate each child section

        if *recursive* is set, iterate all child sections recurively (depth-search)
        """
        if yield_self:
            yield self
        for i in self._sections:
            yield i
            if recursive:
                for j in i.itersections(recursive=recursive):
                    yield j

    def contains(self, obj):
        """
        checks if a subsection of name&type of *obj* is a child of this section
        if so return this child
        """
        for i in self._sections:
            if obj.name == i.name and obj.type == i.type:
                return i

    def _matches(self, obj, key=None, type=None):
        return (key is None or (key is not None and hasattr(obj, "name") and obj.name == key)) \
            and (type is None or (type is not None and hasattr(obj, "type") and obj.type == type))

    def find_by_path(self, path):
        """
        find a Section/Property through a path like "name1/name2"
        """
        path = path.split("/")
        return self._find_by_path(path)

    def _find_by_path(self, path):
        """
        find a Section/Property through a path like ("name1", "name2")
        """
        cur = self
        for i in path:
            if i == "." or i == "": continue
            if i == "..":
                cur = cur.parent
                continue
            cur = cur[i]
        return cur

    def find(self, key=None, type=None, findAll=False):
        """return the first subsection named *key* of type *type*"""
        ret = []
        for s in self._sections:
            if self._matches(s, key, type):
                if findAll:
                    ret.append(s)
                else:
                    return s
        if ret:
            return ret

    def find_related(self, key=None, type=None, children=True, siblings=True, parents=True, recursive=True, findAll=False):
        """
        finds a related section named *key* and/or *type*

          * by searching its children’s children if *children* is True
            if *recursive* is true all leave nodes will be searched
          * by searching its siblings if *siblings* is True
          * by searching the parent element if *parents* is True
            if *recursive* is True all parent nodes until the root are searched
          * if *findAll* is True, returns a list of all matching objects
        """
        ret = []

        if children:
            for section in self._sections:
                if self._matches(section, key, type):
                    if findAll:
                        ret.append(section)
                    else:
                        return section

                if recursive:
                    obj = section.find_related(key, type, children, siblings=False, parents=False, recursive=recursive, findAll=findAll)
                    if obj is not None:
                        if findAll:
                            ret += obj
                        else:
                            return obj

        if siblings and self.parent is not None:
            obj = self.parent.find(key, type, findAll)
            if obj is not None:
                if findAll:
                    ret += obj
                else:
                    return obj

        if parents:
            obj = self
            while obj.parent is not None:
                obj = obj.parent
                if self._matches(obj, key, type):
                    if findAll:
                        ret.append(obj)
                    else:
                        return obj
                if not recursive: break

        if ret:
            return ret
        return None

    def get_path(self):
        """
        returns the absolute path of this section
        """
        node = self
        path = []
        while node.parent is not None:
            path.insert(0, node.name)
            node = node.parent
        return "/" + "/".join(path)

    @staticmethod
    def _get_relative_path(a, b):
        """
        returns a relative path for navigation from dir *a* to dir *b*

        if the common parent of both is "/", return an absolute path
        """
        a += "/"
        b += "/"
        parent = posixpath.dirname(posixpath.commonprefix([a,b]))
        if parent == "/": return b[:-1]

        a = posixpath.relpath(a, parent)
        b = posixpath.relpath(b, parent)
        if a == ".": return b

        return posixpath.normpath("../" * (a.count("/")+1) + b)

    def get_relative_path(self, section):
        """
        returns a relative (file)path to point to section (e.g. ../other_section)

        if the common parent of both sections is the document (i.e. /), return an absolute path
        """
        a = self.get_path()
        b = section.get_path()
        return self._get_relative_path(a,b)

    def clean(self):
        for i in self:
            i.clean()

    def clone(self, children=True):
        """
        clone this object recursively allowing to copy it independently
        to another document
        """
        obj = super(sectionable, self).clone(children)
        obj._parent = None
        obj._sections = SmartList()
        if children:
            for s in self._sections:
                obj.append(s.clone())

        return obj

    @property
    def repository(self):
        return self._repository

    @repository.setter
    def repository(self, url):
        if not url: url = None
        self._repository = url
        if url:
            terminology.deferred_load(url)

    def get_repository(self):
        return self._repository
<|endoftext|>


<commit_before>import os
import sys
from googletrans import Translator
import pycountry

sp = os.path.dirname(os.path.realpath(sys.argv[0]))

import maincore as core

def run(message, prefix, aliasName):
    cmdlen = len(prefix + aliasName)
    opstring = message.content[cmdlen:].strip()
    translator = Translator()
    splitString = opstring.split()
    modifiers = [ word for word in splitString if word[0]=='-' ]
    for n, i in enumerate(modifiers):
        splitString.remove(i)
        modifiers[n] = i[1:]
    opstring = " ".join(splitString)
    try:
        if len(modifiers) == 0:
            done = translator.translate(opstring)
        elif len(modifiers) == 1:
            done = translator.translate(opstring, dest=modifiers[0])
        else:
            done = translator.translate(opstring, src=modifiers[0], dest=modifiers[1])
    except Exception as e:
        core.send(message.channel, "Something failed while translating. Please ensure you are using codes for indicating language, such as `en` or `ja`, or use the full language name, such as `nowegian` or `german`\n\nError:{}".format(e))
        return
    try:
        fromlang = pycountry.languages.get(alpha_2=done.src).name
    except:
        fromlang = done.src
    try:
        tolang = pycountry.languages.get(alpha_2=done.dest).name
    except:
        tolang = done.dest
    core.send(message.channel, ":: translating {} -> {}::\n{}\n({})".format(fromlang, tolang, done.text, done.pronunciation), "```asciidoc\n", "\n```")

def help_use():
    return "Translate something from a language to another language, using Google Translate"

def help_param():
    return "[WORDS**] The word(s) or sentence(s) to translate into English\nPrefix words with `-` to mark the target or destination language. These are optional."

def help_cmd(prefix):
    return prefix + "translate [WORDS**]"

def help_perms():
    return 0

def help_list():
    return "Translate something."

def aliasName():
    return ['translate', 'tr', 'atr']
<commit_msg>Added more clarity to the help text of the translate command<commit_after>import os
import sys
from googletrans import Translator
import pycountry

sp = os.path.dirname(os.path.realpath(sys.argv[0]))

import maincore as core

def run(message, prefix, aliasName):
    cmdlen = len(prefix + aliasName)
    opstring = message.content[cmdlen:].strip()
    translator = Translator()
    splitString = opstring.split()
    modifiers = [ word for word in splitString if word[0]=='-' ]
    for n, i in enumerate(modifiers):
        splitString.remove(i)
        modifiers[n] = i[1:]
    opstring = " ".join(splitString)
    try:
        if len(modifiers) == 0:
            done = translator.translate(opstring)
        elif len(modifiers) == 1:
            done = translator.translate(opstring, dest=modifiers[0])
        else:
            done = translator.translate(opstring, src=modifiers[0], dest=modifiers[1])
    except Exception as e:
        core.send(message.channel, "Something failed while translating. Please ensure you are using codes for indicating language, such as `en` or `ja`, or use the full language name, such as `nowegian` or `german`\n\nError:{}".format(e))
        return
    try:
        fromlang = pycountry.languages.get(alpha_2=done.src).name
    except:
        fromlang = done.src
    try:
        tolang = pycountry.languages.get(alpha_2=done.dest).name
    except:
        tolang = done.dest
    core.send(message.channel, ":: translating {} -> {}::\n{}\n({})".format(fromlang, tolang, done.text, done.pronunciation), "```asciidoc\n", "\n```")

def help_use():
    return "Translate something from a language to another language, using Google Translate"

def help_param():
    return "[WORDS**] The word(s) or sentence(s) to translate into English\nPrefix words with `-` to mark the target or destination language. These are optional.
k!tr text               = guess language and translate to English
k!tr -lang text         = translate text to lang
k!tr -lang1 -lang2 text = translate text from lang1 to lang2 "

def help_cmd(prefix):
    return prefix + "translate [WORDS**]"

def help_perms():
    return 0

def help_list():
    return "Translate something."

def aliasName():
    return ['translate', 'tr', 'atr']
<|endoftext|>


<commit_before>"""
test the release system
"""
from rez.build_process_ import create_build_process
from rez.build_system import create_build_system
from rez.resolved_context import ResolvedContext
from rez.release_vcs import create_release_vcs
from rez.packages_ import iter_packages
from rez.vendor import yaml
from rez.system import system
from rez.exceptions import BuildError, ReleaseError, ReleaseVCSError
import rez.vendor.unittest2 as unittest
from rez.tests.util import TestBase, TempdirMixin, shell_dependent, \
    install_dependent
from rez.package_serialise import dump_package_data
from rez.serialise import FileFormat
import rez.bind.platform
import rez.bind.arch
import rez.bind.os
import rez.bind.python
import shutil
import os.path

# TODO: variant-based test

class TestRelease(TestBase, TempdirMixin):
    @classmethod
    def setUpClass(cls):
        TempdirMixin.setUpClass()

        path = os.path.dirname(__file__)
        cls.src_path = os.path.join(path, "data", "release")
        cls.src_root = os.path.join(cls.root, "src")
        cls.install_root = os.path.join(cls.root, "packages")

        cls.settings = dict(
            packages_path=[cls.install_root],
            package_filter=None,
            release_packages_path=cls.install_root,
            resolve_caching=False,
            warn_untimestamped=False,
            implicit_packages=[])

    @classmethod
    def tearDownClass(cls):
        TempdirMixin.tearDownClass()

    @classmethod
    def _create_context(cls, *pkgs):
        return ResolvedContext(pkgs)

    def _setup_release(self):
       # start fresh
        system.clear_caches()
        if os.path.exists(self.install_root):
            shutil.rmtree(self.install_root)
        if os.path.exists(self.src_root):
            shutil.rmtree(self.src_root)
        shutil.copytree(self.src_path, self.src_root)

        self.packagefile = os.path.join(self.src_root, "package.yaml")
        with open(self.packagefile) as f:
            self.package_data = yaml.load(f.read())

        # create the build system
        self.buildsys = create_build_system(self.src_root, verbose=True)
        self.assertEqual(self.buildsys.name(), "bez")

        # create the vcs - should error because stub file doesn't exist yet
        with self.assertRaises(ReleaseVCSError):
            create_release_vcs(self.src_root)

        # make the stub file
        self.stubfile = os.path.join(self.src_root, ".stub")
        with open(self.stubfile, 'w'):
            pass

        # create the vcs - should work now
        self.vcs = create_release_vcs(self.src_root)
        self.assertEqual(self.vcs.name(), "stub")

    def _write_package(self):
        with open(self.packagefile, 'w') as f:
            dump_package_data(self.package_data, f, format_=FileFormat.yaml)

    def _create_builder(self, ensure_latest=True):
        return create_build_process(process_type="local",
                                    working_dir=self.src_root,
                                    build_system=self.buildsys,
                                    vcs=self.vcs,
                                    ensure_latest=ensure_latest,
                                    ignore_existing_tag=True,
                                    verbose=True)

    def assertVariantsEqual(self, vars1, vars2):
        """Utility function to compare string-variants with formal lists of
        "PackageRequest" objects
        """
        from rez.utils.formatting import PackageRequest

        def _standardize_variants(variants):
            return [list(str(req) for req in variant) for variant in variants]

        self.assertEqual(_standardize_variants(vars1),
                         _standardize_variants(vars2))

    @shell_dependent()
    @install_dependent
    def test_1(self):
        """Basic release."""
        # release should fail because release path does not exist
        self._setup_release()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # release should work this time
        os.mkdir(self.install_root)
        builder.release()

        # check a file to see the release made it
        filepath = os.path.join(self.install_root,
                                "foo", "1.0", "data", "data.txt")
        self.assertTrue(os.path.exists(filepath))

        # failed release (same version released again)
        builder = self._create_builder()
        num_variants = builder.release()
        self.assertEqual(num_variants, 0)

        # update package version and release again
        self.package_data["version"] = "1.1"
        self._write_package()
        builder = self._create_builder()
        builder.release()

        # change version to earlier and do failed release attempt
        self.package_data["version"] = "1.0.1"
        self._write_package()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # release again, this time allow not latest
        builder = self._create_builder(ensure_latest=False)
        builder.release()

        # change uuid and do failed release attempt
        self.package_data["version"] = "1.2"
        self.package_data["uuid"] += "_CHANGED"
        self._write_package()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # check the vcs contains the tags we expect
        expected_value = set(["foo-1.0", "foo-1.0.1", "foo-1.1"])
        with open(self.stubfile) as f:
            stub_data = yaml.load(f.read())
        tags = set(stub_data.get("tags", {}).keys())
        self.assertEqual(tags, expected_value)

        # check the package install path contains the packages we expect
        it = iter_packages("foo", paths=[self.install_root])
        qnames = set(x.qualified_name for x in it)
        self.assertEqual(qnames, expected_value)

    def test_2_variant_add(self):
        """Test variant installation on release
        """
        self.src_path = os.path.join(self.src_path, "variants")
        self._setup_release()

        # copy the spangle package onto the packages path
        os.mkdir(self.install_root)
        shutil.copytree(os.path.join(self.src_root, 'spangle'),
                        os.path.join(self.install_root, 'spangle'))

        # release the bar package, which has spangle-1.0 and 1.1 variants
        builder = self._create_builder()
        builder.release()

        # check that the released package has two variants, and the "old"
        # description...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1']])
        self.assertEqual(rel_package.description,
                         'a package with two variants')

        # now, change the package so it has a single spangle-2.0 variant...
        self.package_data['variants'] = [['spangle-2.0']]
        new_desc = 'added spangle-2.0 variant'
        self.package_data['description'] = new_desc
        self._write_package()

        # ...then try to re-release
        builder = self._create_builder()
        builder.release()

        # check that the released package now three variants, and the "new"
        # description...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1'],
                                                        ['spangle-2.0']])
        self.assertEqual(rel_package.description, new_desc)

        # finally, release a package that contains a variant already released,
        # but with a different index...
        self.package_data['variants'] = [['spangle-1.1']]
        third_desc = 'releasing with already existing variant'
        self.package_data['description'] = third_desc
        self._write_package()
        builder = self._create_builder()
        builder.release()

        # make sure that the variant indices stayed the same...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1'],
                                                        ['spangle-2.0']])
        # ...but that the description was updated
        self.assertEqual(rel_package.description, third_desc)

if __name__ == '__main__':
    unittest.main()

# Copyright 2013-2016 Allan Johns.
#
# This library is free software: you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation, either
# version 3 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.  If not, see <http://www.gnu.org/licenses/>.
<commit_msg>New test breaks unittests if install has not yet occured<commit_after>"""
test the release system
"""
from rez.build_process_ import create_build_process
from rez.build_system import create_build_system
from rez.resolved_context import ResolvedContext
from rez.release_vcs import create_release_vcs
from rez.packages_ import iter_packages
from rez.vendor import yaml
from rez.system import system
from rez.exceptions import BuildError, ReleaseError, ReleaseVCSError
import rez.vendor.unittest2 as unittest
from rez.tests.util import TestBase, TempdirMixin, shell_dependent, \
    install_dependent
from rez.package_serialise import dump_package_data
from rez.serialise import FileFormat
import rez.bind.platform
import rez.bind.arch
import rez.bind.os
import rez.bind.python
import shutil
import os.path

# TODO: variant-based test

class TestRelease(TestBase, TempdirMixin):
    @classmethod
    def setUpClass(cls):
        TempdirMixin.setUpClass()

        path = os.path.dirname(__file__)
        cls.src_path = os.path.join(path, "data", "release")
        cls.src_root = os.path.join(cls.root, "src")
        cls.install_root = os.path.join(cls.root, "packages")

        cls.settings = dict(
            packages_path=[cls.install_root],
            package_filter=None,
            release_packages_path=cls.install_root,
            resolve_caching=False,
            warn_untimestamped=False,
            implicit_packages=[])

    @classmethod
    def tearDownClass(cls):
        TempdirMixin.tearDownClass()

    @classmethod
    def _create_context(cls, *pkgs):
        return ResolvedContext(pkgs)

    def _setup_release(self):
       # start fresh
        system.clear_caches()
        if os.path.exists(self.install_root):
            shutil.rmtree(self.install_root)
        if os.path.exists(self.src_root):
            shutil.rmtree(self.src_root)
        shutil.copytree(self.src_path, self.src_root)

        self.packagefile = os.path.join(self.src_root, "package.yaml")
        with open(self.packagefile) as f:
            self.package_data = yaml.load(f.read())

        # create the build system
        self.buildsys = create_build_system(self.src_root, verbose=True)
        self.assertEqual(self.buildsys.name(), "bez")

        # create the vcs - should error because stub file doesn't exist yet
        with self.assertRaises(ReleaseVCSError):
            create_release_vcs(self.src_root)

        # make the stub file
        self.stubfile = os.path.join(self.src_root, ".stub")
        with open(self.stubfile, 'w'):
            pass

        # create the vcs - should work now
        self.vcs = create_release_vcs(self.src_root)
        self.assertEqual(self.vcs.name(), "stub")

    def _write_package(self):
        with open(self.packagefile, 'w') as f:
            dump_package_data(self.package_data, f, format_=FileFormat.yaml)

    def _create_builder(self, ensure_latest=True):
        return create_build_process(process_type="local",
                                    working_dir=self.src_root,
                                    build_system=self.buildsys,
                                    vcs=self.vcs,
                                    ensure_latest=ensure_latest,
                                    ignore_existing_tag=True,
                                    verbose=True)

    def assertVariantsEqual(self, vars1, vars2):
        """Utility function to compare string-variants with formal lists of
        "PackageRequest" objects
        """
        from rez.utils.formatting import PackageRequest

        def _standardize_variants(variants):
            return [list(str(req) for req in variant) for variant in variants]

        self.assertEqual(_standardize_variants(vars1),
                         _standardize_variants(vars2))

    @shell_dependent()
    @install_dependent
    def test_1(self):
        """Basic release."""
        # release should fail because release path does not exist
        self._setup_release()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # release should work this time
        os.mkdir(self.install_root)
        builder.release()

        # check a file to see the release made it
        filepath = os.path.join(self.install_root,
                                "foo", "1.0", "data", "data.txt")
        self.assertTrue(os.path.exists(filepath))

        # failed release (same version released again)
        builder = self._create_builder()
        num_variants = builder.release()
        self.assertEqual(num_variants, 0)

        # update package version and release again
        self.package_data["version"] = "1.1"
        self._write_package()
        builder = self._create_builder()
        builder.release()

        # change version to earlier and do failed release attempt
        self.package_data["version"] = "1.0.1"
        self._write_package()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # release again, this time allow not latest
        builder = self._create_builder(ensure_latest=False)
        builder.release()

        # change uuid and do failed release attempt
        self.package_data["version"] = "1.2"
        self.package_data["uuid"] += "_CHANGED"
        self._write_package()
        builder = self._create_builder()
        with self.assertRaises(ReleaseError):
            builder.release()

        # check the vcs contains the tags we expect
        expected_value = set(["foo-1.0", "foo-1.0.1", "foo-1.1"])
        with open(self.stubfile) as f:
            stub_data = yaml.load(f.read())
        tags = set(stub_data.get("tags", {}).keys())
        self.assertEqual(tags, expected_value)

        # check the package install path contains the packages we expect
        it = iter_packages("foo", paths=[self.install_root])
        qnames = set(x.qualified_name for x in it)
        self.assertEqual(qnames, expected_value)

    @shell_dependent()
    @install_dependent
    def test_2_variant_add(self):
        """Test variant installation on release
        """
        self.src_path = os.path.join(self.src_path, "variants")
        self._setup_release()

        # copy the spangle package onto the packages path
        os.mkdir(self.install_root)
        shutil.copytree(os.path.join(self.src_root, 'spangle'),
                        os.path.join(self.install_root, 'spangle'))

        # release the bar package, which has spangle-1.0 and 1.1 variants
        builder = self._create_builder()
        builder.release()

        # check that the released package has two variants, and the "old"
        # description...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1']])
        self.assertEqual(rel_package.description,
                         'a package with two variants')

        # now, change the package so it has a single spangle-2.0 variant...
        self.package_data['variants'] = [['spangle-2.0']]
        new_desc = 'added spangle-2.0 variant'
        self.package_data['description'] = new_desc
        self._write_package()

        # ...then try to re-release
        builder = self._create_builder()
        builder.release()

        # check that the released package now three variants, and the "new"
        # description...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1'],
                                                        ['spangle-2.0']])
        self.assertEqual(rel_package.description, new_desc)

        # finally, release a package that contains a variant already released,
        # but with a different index...
        self.package_data['variants'] = [['spangle-1.1']]
        third_desc = 'releasing with already existing variant'
        self.package_data['description'] = third_desc
        self._write_package()
        builder = self._create_builder()
        builder.release()

        # make sure that the variant indices stayed the same...
        rel_packages = list(iter_packages("bar", paths=[self.install_root]))
        self.assertEqual(len(rel_packages), 1)
        rel_package = rel_packages[0]
        self.assertVariantsEqual(rel_package.variants, [['spangle-1.0'],
                                                        ['spangle-1.1'],
                                                        ['spangle-2.0']])
        # ...but that the description was updated
        self.assertEqual(rel_package.description, third_desc)

if __name__ == '__main__':
    unittest.main()

# Copyright 2013-2016 Allan Johns.
#
# This library is free software: you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation, either
# version 3 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.  If not, see <http://www.gnu.org/licenses/>.
<|endoftext|>