示例#1
0
    def load_workers(self, filename, config_dict):
        config_valid = True

        deprecated_workers = config_dict.get('slaves')
        if deprecated_workers is not None:
            reportDeprecatedWorkerNameUsage(
                "c['slaves'] key is deprecated, use c['workers'] instead",
                filename=filename)
            if not self._check_workers(deprecated_workers, "c['slaves']"):
                config_valid = False

        workers = config_dict.get('workers')
        if workers is not None:
            if not self._check_workers(workers, "c['workers']"):
                config_valid = False

        if deprecated_workers is not None and workers is not None:
            error("Use of c['workers'] and c['slaves'] at the same time is "
                  "not supported. Use only c['workers'] instead")
            return

        if not config_valid:
            return

        elif deprecated_workers is not None or workers is not None:
            self.workers = []
            if deprecated_workers is not None:
                self.workers.extend(deprecated_workers)
            if workers is not None:
                self.workers.extend(workers)

        else:
            pass
示例#2
0
    def __init__(self, s, workerdest=None,
                 workdir=None, maxsize=None, blocksize=16 * 1024, mode=None,
                 slavedest=None,  # deprecated, use `workerdest` instead
                 **buildstep_kwargs):
        # Deprecated API support.
        if slavedest is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavedest' keyword argument is deprecated, "
                "use 'workerdest' instead")
            assert workerdest is None
            workerdest = slavedest

        # Emulate that first two arguments are positional.
        if workerdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.s = s
        self.workerdest = workerdest
        self._registerOldWorkerAttr("workerdest")
        self.maxsize = maxsize
        self.blocksize = blocksize
        if not isinstance(mode, (int, type(None))):
            config.error(
                "StringDownload step's mode must be an integer or None,"
                " got '%s'" % mode)
        self.mode = mode
示例#3
0
    def __init__(
            self,
            name,
            maxCount=1,
            maxCountForWorker=None,
            # deprecated, use `maxCountForWorker` instead
            maxCountForSlave=None):
        # Deprecated API support.
        if maxCountForSlave is not None:
            reportDeprecatedWorkerNameUsage(
                "'maxCountForSlave' keyword argument is deprecated, "
                "use 'maxCountForWorker' instead")
            assert maxCountForWorker is None
            maxCountForWorker = maxCountForSlave

        self.name = name
        self.maxCount = maxCount
        if maxCountForWorker is None:
            maxCountForWorker = {}
        self.maxCountForWorker = maxCountForWorker
        self._registerOldWorkerAttr("maxCountForWorker")
        # for comparison purposes, turn this dictionary into a stably-sorted
        # list of tuples
        self._maxCountForWorkerList = tuple(
            sorted(self.maxCountForWorker.items()))
示例#4
0
    def __init__(self, workersrc=None, masterdest=None,
                 workdir=None, maxsize=None, blocksize=16 * 1024,
                 compress=None, url=None,
                 slavesrc=None,  # deprecated, use `workersrc` instead
                 **buildstep_kwargs
                 ):
        # Deprecated API support.
        if slavesrc is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavesrc' keyword argument is deprecated, "
                "use 'workersrc' instead")
            assert workersrc is None
            workersrc = slavesrc

        # Emulate that first two arguments are positional.
        if workersrc is None or masterdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.workersrc = workersrc
        self._registerOldWorkerAttr("workersrc")
        self.masterdest = masterdest
        self.maxsize = maxsize
        self.blocksize = blocksize
        if compress not in (None, 'gz', 'bz2'):
            config.error(
                "'compress' must be one of None, 'gz', or 'bz2'")
        self.compress = compress
        self.url = url
示例#5
0
    def __init__(self, workersrc=None, masterdest=None,
                 workdir=None, maxsize=None, blocksize=16 * 1024, mode=None,
                 keepstamp=False, url=None,
                 slavesrc=None,  # deprecated, use `workersrc` instead
                 **buildstep_kwargs):
        # Deprecated API support.
        if slavesrc is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavesrc' keyword argument is deprecated, "
                "use 'workersrc' instead")
            assert workersrc is None
            workersrc = slavesrc

        # Emulate that first two arguments are positional.
        if workersrc is None or masterdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.workersrc = workersrc
        self._registerOldWorkerAttr("workersrc")
        self.masterdest = masterdest
        self.maxsize = maxsize
        self.blocksize = blocksize
        if not isinstance(mode, (int, type(None))):
            config.error(
                'mode must be an integer or None')
        self.mode = mode
        self.keepstamp = keepstamp
        self.url = url
示例#6
0
    def __init__(
            self,
            s,
            workerdest=None,
            workdir=None,
            maxsize=None,
            blocksize=16 * 1024,
            mode=None,
            slavedest=None,  # deprecated, use `workerdest` instead
            **buildstep_kwargs):
        # Deprecated API support.
        if slavedest is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavedest' keyword argument is deprecated, "
                "use 'workerdest' instead")
            assert workerdest is None
            workerdest = slavedest

        # Emulate that first two arguments are positional.
        if workerdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.s = s
        self.workerdest = workerdest
        self._registerOldWorkerAttr("workerdest")
        self.maxsize = maxsize
        self.blocksize = blocksize
        if not isinstance(mode, (int, type(None))):
            config.error(
                "StringDownload step's mode must be an integer or None,"
                " got '%s'" % mode)
        self.mode = mode
示例#7
0
    def __init__(self, workersrcs=None, masterdest=None,
                 workdir=None, maxsize=None, blocksize=16 * 1024, glob=False,
                 mode=None, compress=None, keepstamp=False, url=None,
                 slavesrcs=None,  # deprecated, use `workersrcs` instead
                 **buildstep_kwargs):
        # Deprecated API support.
        if slavesrcs is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavesrcs' keyword argument is deprecated, "
                "use 'workersrcs' instead")
            assert workersrcs is None
            workersrcs = slavesrcs

        # Emulate that first two arguments are positional.
        if workersrcs is None or masterdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.workersrcs = workersrcs if isinstance(workersrcs, list) else [workersrcs]
        self._registerOldWorkerAttr("workersrcs")
        self.masterdest = masterdest
        self.maxsize = maxsize
        self.blocksize = blocksize
        if not isinstance(mode, (int, type(None))):
            config.error(
                'mode must be an integer or None')
        self.mode = mode
        if compress not in (None, 'gz', 'bz2'):
            config.error(
                "'compress' must be one of None, 'gz', or 'bz2'")
        self.compress = compress
        self.glob = glob
        self.keepstamp = keepstamp
        self.url = url
示例#8
0
    def load_workers(self, filename, config_dict):
        config_valid = True

        deprecated_workers = config_dict.get('slaves')
        if deprecated_workers is not None:
            reportDeprecatedWorkerNameUsage(
                "c['slaves'] key is deprecated, use c['workers'] instead",
                filename=filename)
            if not self._check_workers(deprecated_workers, "c['slaves']"):
                config_valid = False

        workers = config_dict.get('workers')
        if workers is not None:
            if not self._check_workers(workers, "c['workers']"):
                config_valid = False

        if deprecated_workers is not None and workers is not None:
            error("Use of c['workers'] and c['slaves'] at the same time is "
                  "not supported. Use only c['workers'] instead")
            return

        if not config_valid:
            return

        elif deprecated_workers is not None or workers is not None:
            self.workers = []
            if deprecated_workers is not None:
                self.workers.extend(deprecated_workers)
            if workers is not None:
                self.workers.extend(workers)

        else:
            pass
示例#9
0
    def __init__(self, workersrc=None, masterdest=None,
                 workdir=None, maxsize=None, blocksize=16 * 1024, mode=None,
                 keepstamp=False, url=None,
                 slavesrc=None,  # deprecated, use `workersrc` instead
                 **buildstep_kwargs):
        # Deprecated API support.
        if slavesrc is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavesrc' keyword argument is deprecated, "
                "use 'workersrc' instead")
            assert workersrc is None
            workersrc = slavesrc

        # Emulate that first two arguments are positional.
        if workersrc is None or masterdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.workersrc = workersrc
        self._registerOldWorkerAttr("workersrc")
        self.masterdest = masterdest
        self.maxsize = maxsize
        self.blocksize = blocksize
        if not isinstance(mode, (int, type(None))):
            config.error(
                'mode must be an integer or None')
        self.mode = mode
        self.keepstamp = keepstamp
        self.url = url
示例#10
0
文件: db.py 项目: wallrj/buildbot
    def __getattr__(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you accessed 'buildslave.{0}' name).".format(name))

        return _Plugins.__getattr__(self, name)
示例#11
0
    def __init__(
            self,
            o,
            workerdest=None,
            slavedest=None,  # deprecated, use `workerdest` instead
            **buildstep_kwargs):
        # Deprecated API support.
        if slavedest is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavedest' keyword argument is deprecated, "
                "use 'workerdest' instead")
            assert workerdest is None
            workerdest = slavedest

        # Emulate that first two arguments are positional.
        if workerdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        if 's' in buildstep_kwargs:
            del buildstep_kwargs['s']
        s = json.dumps(o)
        StringDownload.__init__(self,
                                s=s,
                                workerdest=workerdest,
                                **buildstep_kwargs)
示例#12
0
文件: db.py 项目: rapyuta/buildbot
    def __getattr__(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you accessed 'buildslave.{0}' name).".format(name))

        return _Plugins.__getattr__(self, name)
示例#13
0
    def __init__(
            self,
            workersrc=None,
            masterdest=None,
            workdir=None,
            maxsize=None,
            blocksize=16 * 1024,
            compress=None,
            url=None,
            slavesrc=None,  # deprecated, use `workersrc` instead
            **buildstep_kwargs):
        # Deprecated API support.
        if slavesrc is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavesrc' keyword argument is deprecated, "
                "use 'workersrc' instead")
            assert workersrc is None
            workersrc = slavesrc

        # Emulate that first two arguments are positional.
        if workersrc is None or masterdest is None:
            raise TypeError("__init__() takes at least 3 arguments")

        _TransferBuildStep.__init__(self, workdir=workdir, **buildstep_kwargs)

        self.workersrc = workersrc
        self._registerOldWorkerAttr("workersrc")
        self.masterdest = masterdest
        self.maxsize = maxsize
        self.blocksize = blocksize
        if compress not in (None, 'gz', 'bz2'):
            config.error("'compress' must be one of None, 'gz', or 'bz2'")
        self.compress = compress
        self.url = url
示例#14
0
文件: db.py 项目: wallrj/buildbot
    def get(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you requested '{0}' name of "
            "'buildbot.plugins.buildslave' plugin).".format(name))

        return _Plugins.get(self, name)
示例#15
0
文件: db.py 项目: wallrj/buildbot
    def __contains__(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you checked is '{0}' name inside "
            "'buildbot.plugins.buildslave').".format(name))

        return _Plugins.__contains__(self, name)
示例#16
0
文件: db.py 项目: wallrj/buildbot
 def value(self):
     reportDeprecatedWorkerNameUsage(
         "'{group}.{compat_name}' is deprecated, "
         "use '{group}.{new_name}' instead".format(
             group=self.group,
             compat_name=self._compat_name,
             new_name=self._new_name))
     return self._plugin_entry.value
示例#17
0
文件: db.py 项目: rapyuta/buildbot
 def value(self):
     reportDeprecatedWorkerNameUsage(
         "'{group}.{compat_name}' is deprecated, "
         "use '{group}.{new_name}' instead".format(
             group=self.group,
             compat_name=self._compat_name,
             new_name=self._new_name))
     return self._plugin_entry.value
示例#18
0
文件: db.py 项目: rapyuta/buildbot
    def __contains__(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you checked is '{0}' name inside "
            "'buildbot.plugins.buildslave').".format(name))

        return _Plugins.__contains__(self, name)
示例#19
0
文件: db.py 项目: rapyuta/buildbot
    def get(self, name):
        reportDeprecatedWorkerNameUsage(
            "'buildbot.plugins.buildslave' plugins namespace is deprecated, "
            "use 'buildbot.plugins.worker' instead "
            "(you requested '{0}' name of "
            "'buildbot.plugins.buildslave' plugin).".format(name))

        return _Plugins.get(self, name)
示例#20
0
    def __init__(self, workdir, command, env=None,
                 want_stdout=1, want_stderr=1,
                 timeout=20 * 60, maxTime=None, sigtermTime=None,
                 logfiles=None, usePTY=None, logEnviron=True,
                 collectStdout=False, collectStderr=False,
                 interruptSignal=None,
                 initialStdin=None, decodeRC=None,
                 stdioLogName='stdio'):
        if logfiles is None:
            logfiles = {}
        if decodeRC is None:
            decodeRC = {0: SUCCESS}
        self.command = command  # stash .command, set it later
        if isinstance(self.command, string_types):
            # Single string command doesn't support obfuscation.
            self.fake_command = command
        else:
            # Try to obfuscate command.
            def obfuscate(arg):
                if isinstance(arg, tuple) and len(arg) == 3 and arg[0] == 'obfuscated':
                    return arg[2]
                else:
                    return arg
            self.fake_command = [obfuscate(c) for c in self.command]

        if env is not None:
            # avoid mutating the original master.cfg dictionary. Each
            # ShellCommand gets its own copy, any start() methods won't be
            # able to modify the original.
            env = env.copy()

        if usePTY == 'slave-config':
            reportDeprecatedWorkerNameUsage(
                "'slave-config' value of 'usePTY' attribute is deprecated, "
                "use None instead.")
            usePTY = None

        args = {'workdir': workdir,
                'env': env,
                'want_stdout': want_stdout,
                'want_stderr': want_stderr,
                'logfiles': logfiles,
                'timeout': timeout,
                'maxTime': maxTime,
                'sigtermTime': sigtermTime,
                'usePTY': usePTY,
                'logEnviron': logEnviron,
                'initial_stdin': initialStdin
                }
        if interruptSignal is not None:
            args['interruptSignal'] = interruptSignal
        RemoteCommand.__init__(self, "shell", args, collectStdout=collectStdout,
                               collectStderr=collectStderr,
                               decodeRC=decodeRC,
                               stdioLogName=stdioLogName)
示例#21
0
文件: ec2.py 项目: wallrj/buildbot
    def create_block_device_mapping(self, mapping_definitions):
        if isinstance(mapping_definitions, list):
            for mapping_definition in mapping_definitions:
                ebs = mapping_definition.get('Ebs')
                if ebs:
                    ebs.setdefault('DeleteOnTermination', True)
            return mapping_definitions

        reportDeprecatedWorkerNameUsage(
            "Use of dict value to 'block_device_map' of EC2LatentWorker "
            "constructor is deprecated. Please use a list matching the AWS API "
            "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html"
        )
        return self._convert_deprecated_block_device_mapping(mapping_definitions)
示例#22
0
文件: ec2.py 项目: Cray/buildbot
    def create_block_device_mapping(self, mapping_definitions):
        if isinstance(mapping_definitions, list):
            for mapping_definition in mapping_definitions:
                ebs = mapping_definition.get('Ebs')
                if ebs:
                    ebs.setdefault('DeleteOnTermination', True)
            return mapping_definitions

        reportDeprecatedWorkerNameUsage(
            "Use of dict value to 'block_device_map' of EC2LatentWorker "
            "constructor is deprecated. Please use a list matching the AWS API "
            "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html"
        )
        return self._convert_deprecated_block_device_mapping(mapping_definitions)
示例#23
0
def _on_property_usage(prop_name, stacklevel):
    """Handle deprecated properties after worker-name transition.

    :param stacklevel: stack level relative to the caller's frame.
    Defaults to caller of the caller of this function.
    """

    # "Remove" current frame
    stacklevel += 1

    deprecated_to_new_props = {"slavename": "workername"}

    if prop_name in deprecated_to_new_props:
        reportDeprecatedWorkerNameUsage(
            "Property '{old_name}' is deprecated, "
            "use '{new_name}' instead.".format(old_name=prop_name, new_name=deprecated_to_new_props[prop_name]),
            stacklevel=stacklevel,
        )
示例#24
0
def _on_property_usage(prop_name, stacklevel):
    """Handle deprecated properties after worker-name transition.

    :param stacklevel: stack level relative to the caller's frame.
    Defaults to caller of the caller of this function.
    """

    # "Remove" current frame
    stacklevel += 1

    deprecated_to_new_props = {'slavename': 'workername'}

    if prop_name in deprecated_to_new_props:
        reportDeprecatedWorkerNameUsage(
            "Property '{old_name}' is deprecated, "
            "use '{new_name}' instead.".format(
                old_name=prop_name,
                new_name=deprecated_to_new_props[prop_name]),
            stacklevel=stacklevel)
示例#25
0
    def __init__(self, workerdest=None,
                 slavedest=None,  # deprecated, use `workerdest` instead
                 **buildstep_kwargs):
        # Deprecated API support.
        if slavedest is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavedest' keyword argument is deprecated, "
                "use 'workerdest' instead")
            assert workerdest is None
            workerdest = slavedest

        # Emulate that first two arguments are positional.
        if workerdest is None:
            raise TypeError("__init__() takes at least 2 arguments")

        self.super_class = StringDownload
        if 's' in buildstep_kwargs:
            del buildstep_kwargs['s']
        StringDownload.__init__(self, s=None, workerdest=workerdest, **buildstep_kwargs)
示例#26
0
    def __init__(self, name, maxCount=1, maxCountForWorker=None,
                 maxCountForSlave=None  # deprecated, use `maxCountForWorker` instead
                 ):
        # Deprecated API support.
        if maxCountForSlave is not None:
            reportDeprecatedWorkerNameUsage(
                "'maxCountForSlave' keyword argument is deprecated, "
                "use 'maxCountForWorker' instead")
            assert maxCountForWorker is None
            maxCountForWorker = maxCountForSlave

        self.name = name
        self.maxCount = maxCount
        if maxCountForWorker is None:
            maxCountForWorker = {}
        self.maxCountForWorker = maxCountForWorker
        self._registerOldWorkerAttr("maxCountForWorker")
        # for comparison purposes, turn this dictionary into a stably-sorted
        # list of tuples
        self._maxCountForWorkerList = tuple(sorted(self.maxCountForWorker.items()))
示例#27
0
文件: ec2.py 项目: BeiNanWoo/buildbot
    def __init__(self, name, password, instance_type, ami=None,
                 valid_ami_owners=None, valid_ami_location_regex=None,
                 elastic_ip=None, identifier=None, secret_identifier=None,
                 aws_id_file_path=None, user_data=None, region=None,
                 keypair_name=None,
                 security_name=None,
                 spot_instance=False, max_spot_price=1.6, volumes=None,
                 placement=None, price_multiplier=1.2, tags=None, retry=1,
                 retry_price_adjustment=1, product_description='Linux/UNIX',
                 subnet_id=None, security_group_ids=None, instance_profile_name=None,
                 block_device_map=None,
                 **kwargs):

        if not boto:
            config.error("The python module 'boto' is needed to use a "
                         "EC2LatentWorker")

        if keypair_name is None:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'keypair_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            keypair_name = 'latent_buildbot_slave'
        if security_name is None and not subnet_id:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'security_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            security_name = 'latent_buildbot_slave'

        if volumes is None:
            volumes = []

        if tags is None:
            tags = {}

        AbstractLatentWorker.__init__(self, name, password, **kwargs)

        if security_name and subnet_id:
            raise ValueError(
                'security_name (EC2 classic security groups) is not supported '
                'in a VPC.  Use security_group_ids instead.')
        if not ((ami is not None) ^
                (valid_ami_owners is not None or
                 valid_ami_location_regex is not None)):
            raise ValueError(
                'You must provide either a specific ami, or one or both of '
                'valid_ami_location_regex and valid_ami_owners')
        self.ami = ami
        if valid_ami_owners is not None:
            if isinstance(valid_ami_owners, (int, long)):
                valid_ami_owners = (valid_ami_owners,)
            else:
                for element in valid_ami_owners:
                    if not isinstance(element, (int, long)):
                        raise ValueError(
                            'valid_ami_owners should be int or iterable '
                            'of ints', element)
        if valid_ami_location_regex is not None:
            if not isinstance(valid_ami_location_regex, basestring):
                raise ValueError(
                    'valid_ami_location_regex should be a string')
            else:
                # verify that regex will compile
                re.compile(valid_ami_location_regex)
        self.valid_ami_owners = valid_ami_owners
        self.valid_ami_location_regex = valid_ami_location_regex
        self.instance_type = instance_type
        self.keypair_name = keypair_name
        self.security_name = security_name
        self.user_data = user_data
        self.spot_instance = spot_instance
        self.max_spot_price = max_spot_price
        self.volumes = volumes
        self.price_multiplier = price_multiplier
        self.retry_price_adjustment = retry_price_adjustment
        self.retry = retry
        self.attempt = 1
        self.product_description = product_description

        if None not in [placement, region]:
            self.placement = '%s%s' % (region, placement)
        else:
            self.placement = None
        if identifier is None:
            assert secret_identifier is None, (
                'supply both or neither of identifier, secret_identifier')
            if aws_id_file_path is None:
                home = os.environ['HOME']
                default_path = os.path.join(home, '.ec2', 'aws_id')
                if os.path.exists(default_path):
                    aws_id_file_path = default_path
            if aws_id_file_path:
                log.msg('WARNING: EC2LatentWorker is using deprecated '
                        'aws_id file')
                with open(aws_id_file_path, 'r') as aws_file:
                    identifier = aws_file.readline().strip()
                    secret_identifier = aws_file.readline().strip()
        else:
            assert aws_id_file_path is None, \
                'if you supply the identifier and secret_identifier, ' \
                'do not specify the aws_id_file_path'
            assert secret_identifier is not None, \
                'supply both or neither of identifier, secret_identifier'

        region_found = None

        # Make the EC2 connection.
        if region is not None:
            for r in boto.ec2.regions(aws_access_key_id=identifier,
                                      aws_secret_access_key=secret_identifier):

                if r.name == region:
                    region_found = r

            if region_found is not None:
                self.ec2_conn = boto.ec2.connect_to_region(region,
                                                           aws_access_key_id=identifier,
                                                           aws_secret_access_key=secret_identifier)
            else:
                raise ValueError(
                    'The specified region does not exist: ' + region)

        else:
            self.ec2_conn = boto.connect_ec2(identifier, secret_identifier)

        # Make a keypair
        #
        # We currently discard the keypair data because we don't need it.
        # If we do need it in the future, we will always recreate the keypairs
        # because there is no way to
        # programmatically retrieve the private key component, unless we
        # generate it and store it on the filesystem, which is an unnecessary
        # usage requirement.
        try:
            key_pair = self.ec2_conn.get_all_key_pairs(keypair_name)[0]
            assert key_pair
            # key_pair.delete() # would be used to recreate
        except boto.exception.EC2ResponseError as e:
            if 'InvalidKeyPair.NotFound' not in e.body:
                if 'AuthFailure' in e.body:
                    log.msg('POSSIBLE CAUSES OF ERROR:\n'
                            '  Did you supply your AWS credentials?\n'
                            '  Did you sign up for EC2?\n'
                            '  Did you put a credit card number in your AWS '
                            'account?\n'
                            'Please doublecheck before reporting a problem.\n')
                raise
            # make one; we would always do this, and stash the result, if we
            # needed the key (for instance, to SSH to the box).  We'd then
            # use paramiko to use the key to connect.
            self.ec2_conn.create_key_pair(keypair_name)

        # create security group
        if security_name:
            try:
                group = self.ec2_conn.get_all_security_groups(security_name)[0]
                assert group
            except boto.exception.EC2ResponseError as e:
                if 'InvalidGroup.NotFound' in e.body:
                    self.security_group = self.ec2_conn.create_security_group(
                        security_name,
                        'Authorization to access the buildbot instance.')
                    # Authorize the master as necessary
                    # TODO this is where we'd open the hole to do the reverse pb
                    # connect to the buildbot
                    # ip = urllib.urlopen(
                    #     'http://checkip.amazonaws.com').read().strip()
                    # self.security_group.authorize('tcp', 22, 22, '%s/32' % ip)
                    # self.security_group.authorize('tcp', 80, 80, '%s/32' % ip)
                else:
                    raise

        # get the image
        if self.ami is not None:
            self.image = self.ec2_conn.get_image(self.ami)
        else:
            # verify we have access to at least one acceptable image
            discard = self.get_image()
            assert discard

        # get the specified elastic IP, if any
        if elastic_ip is not None:
            elastic_ip = self.ec2_conn.get_all_addresses([elastic_ip])[0]
        self.elastic_ip = elastic_ip
        self.subnet_id = subnet_id
        self.security_group_ids = security_group_ids
        self.classic_security_groups = [self.security_name] if self.security_name else None
        self.instance_profile_name = instance_profile_name
        self.tags = tags
        self.block_device_map = self.create_block_device_mapping(block_device_map)
示例#28
0
文件: ec2.py 项目: Cray/buildbot
    def __init__(self, name, password, instance_type, ami=None,
                 valid_ami_owners=None, valid_ami_location_regex=None,
                 elastic_ip=None, identifier=None, secret_identifier=None,
                 aws_id_file_path=None, user_data=None, region=None,
                 keypair_name=None,
                 security_name=None,
                 spot_instance=False, max_spot_price=1.6, volumes=None,
                 placement=None, price_multiplier=1.2, tags=None,
                 product_description='Linux/UNIX',
                 subnet_id=None, security_group_ids=None, instance_profile_name=None,
                 block_device_map=None, session=None,
                 **kwargs):

        if not boto3:
            config.error("The python module 'boto3' is needed to use a "
                         "EC2LatentWorker")

        if keypair_name is None:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'keypair_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            keypair_name = 'latent_buildbot_slave'
        if security_name is None and not subnet_id:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'security_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            security_name = 'latent_buildbot_slave'

        if volumes is None:
            volumes = []

        if tags is None:
            tags = {}

        AbstractLatentWorker.__init__(self, name, password, **kwargs)

        if security_name and subnet_id:
            raise ValueError(
                'security_name (EC2 classic security groups) is not supported '
                'in a VPC.  Use security_group_ids instead.')
        if not ((ami is not None) ^
                (valid_ami_owners is not None or
                 valid_ami_location_regex is not None)):
            raise ValueError(
                'You must provide either a specific ami, or one or both of '
                'valid_ami_location_regex and valid_ami_owners')
        self.ami = ami
        if valid_ami_owners is not None:
            if isinstance(valid_ami_owners, integer_types):
                valid_ami_owners = (valid_ami_owners,)
            else:
                for element in valid_ami_owners:
                    if not isinstance(element, integer_types):
                        raise ValueError(
                            'valid_ami_owners should be int or iterable '
                            'of ints', element)
        if valid_ami_location_regex is not None:
            if not isinstance(valid_ami_location_regex, string_types):
                raise ValueError(
                    'valid_ami_location_regex should be a string')
            else:
                # verify that regex will compile
                re.compile(valid_ami_location_regex)
        if spot_instance and price_multiplier is None and max_spot_price is None:
            raise ValueError('You must provide either one, or both, of '
                             'price_multiplier or max_spot_price')
        self.valid_ami_owners = None
        if valid_ami_owners:
            self.valid_ami_owners = [str(o) for o in valid_ami_owners]
        self.valid_ami_location_regex = valid_ami_location_regex
        self.instance_type = instance_type
        self.keypair_name = keypair_name
        self.security_name = security_name
        self.user_data = user_data
        self.spot_instance = spot_instance
        self.max_spot_price = max_spot_price
        self.volumes = volumes
        self.price_multiplier = price_multiplier
        self.product_description = product_description

        if None not in [placement, region]:
            self.placement = '%s%s' % (region, placement)
        else:
            self.placement = None
        if identifier is None:
            assert secret_identifier is None, (
                'supply both or neither of identifier, secret_identifier')
            if aws_id_file_path is None:
                home = os.environ['HOME']
                default_path = os.path.join(home, '.ec2', 'aws_id')
                if os.path.exists(default_path):
                    aws_id_file_path = default_path
            if aws_id_file_path:
                log.msg('WARNING: EC2LatentWorker is using deprecated '
                        'aws_id file')
                with open(aws_id_file_path, 'r') as aws_file:
                    identifier = aws_file.readline().strip()
                    secret_identifier = aws_file.readline().strip()
        else:
            assert aws_id_file_path is None, \
                'if you supply the identifier and secret_identifier, ' \
                'do not specify the aws_id_file_path'
            assert secret_identifier is not None, \
                'supply both or neither of identifier, secret_identifier'

        region_found = None

        # Make the EC2 connection.
        self.session = session
        if self.session is None:
            if region is not None:
                for r in boto3.Session(
                        aws_access_key_id=identifier,
                        aws_secret_access_key=secret_identifier).get_available_regions('ec2'):

                    if r == region:
                        region_found = r

                if region_found is not None:
                    self.session = boto3.Session(
                        region_name=region,
                        aws_access_key_id=identifier,
                        aws_secret_access_key=secret_identifier)
                else:
                    raise ValueError(
                        'The specified region does not exist: ' + region)

            else:
                # boto2 defaulted to us-east-1 when region was unset, we
                # mimic this here in boto3
                region = botocore.session.get_session().get_config_variable('region')
                if region is None:
                    region = 'us-east-1'
                self.session = boto3.Session(
                    aws_access_key_id=identifier,
                    aws_secret_access_key=secret_identifier,
                    region_name=region
                )

        self.ec2 = self.session.resource('ec2')
        self.ec2_client = self.session.client('ec2')

        # Make a keypair
        #
        # We currently discard the keypair data because we don't need it.
        # If we do need it in the future, we will always recreate the keypairs
        # because there is no way to
        # programmatically retrieve the private key component, unless we
        # generate it and store it on the filesystem, which is an unnecessary
        # usage requirement.
        try:
            self.ec2.KeyPair(self.keypair_name).load()
            # key_pair.delete() # would be used to recreate
        except ClientError as e:
            if 'InvalidKeyPair.NotFound' not in str(e):
                if 'AuthFailure' in str(e):
                    log.msg('POSSIBLE CAUSES OF ERROR:\n'
                            '  Did you supply your AWS credentials?\n'
                            '  Did you sign up for EC2?\n'
                            '  Did you put a credit card number in your AWS '
                            'account?\n'
                            'Please doublecheck before reporting a problem.\n')
                raise
            # make one; we would always do this, and stash the result, if we
            # needed the key (for instance, to SSH to the box).  We'd then
            # use paramiko to use the key to connect.
            self.ec2.create_key_pair(KeyName=keypair_name)

        # create security group
        if security_name:
            try:
                self.ec2_client.describe_security_groups(GroupNames=[security_name])
            except ClientError as e:
                if 'InvalidGroup.NotFound' in str(e):
                    self.security_group = self.ec2.create_security_group(
                        GroupName=security_name,
                        Description='Authorization to access the buildbot instance.')
                    # Authorize the master as necessary
                    # TODO this is where we'd open the hole to do the reverse pb
                    # connect to the buildbot
                    # ip = urllib.urlopen(
                    #     'http://checkip.amazonaws.com').read().strip()
                    # self.security_group.authorize('tcp', 22, 22, '%s/32' % ip)
                    # self.security_group.authorize('tcp', 80, 80, '%s/32' % ip)
                else:
                    raise

        # get the image
        if self.ami is not None:
            self.image = self.ec2.Image(self.ami)
        else:
            # verify we have access to at least one acceptable image
            discard = self.get_image()
            assert discard

        # get the specified elastic IP, if any
        if elastic_ip is not None:
            # Using ec2.vpc_addresses.filter(PublicIps=[elastic_ip]) throws a
            # NotImplementedError("Filtering not supported in describe_address.") in moto
            # https://github.com/spulec/moto/blob/100ec4e7c8aa3fde87ff6981e2139768816992e4/moto/ec2/responses/elastic_ip_addresses.py#L52
            addresses = self.ec2.meta.client.describe_addresses(
                PublicIps=[elastic_ip])['Addresses']
            if not addresses:
                raise ValueError(
                    'Could not find EIP for IP: ' + elastic_ip)
            allocation_id = addresses[0]['AllocationId']
            elastic_ip = self.ec2.VpcAddress(allocation_id)
        self.elastic_ip = elastic_ip
        self.subnet_id = subnet_id
        self.security_group_ids = security_group_ids
        self.classic_security_groups = [
            self.security_name] if self.security_name else None
        self.instance_profile_name = instance_profile_name
        self.tags = tags
        self.block_device_map = self.create_block_device_mapping(
            block_device_map) if block_device_map else None
示例#29
0
    def __init__(self,
                 workdir,
                 command,
                 env=None,
                 want_stdout=1,
                 want_stderr=1,
                 timeout=20 * 60,
                 maxTime=None,
                 sigtermTime=None,
                 logfiles=None,
                 usePTY=None,
                 logEnviron=True,
                 collectStdout=False,
                 collectStderr=False,
                 interruptSignal=None,
                 initialStdin=None,
                 decodeRC=None,
                 stdioLogName='stdio'):
        if logfiles is None:
            logfiles = {}
        if decodeRC is None:
            decodeRC = {0: SUCCESS}
        self.command = command  # stash .command, set it later
        if isinstance(self.command, string_types):
            # Single string command doesn't support obfuscation.
            self.fake_command = command
        else:
            # Try to obfuscate command.
            def obfuscate(arg):
                if isinstance(
                        arg,
                        tuple) and len(arg) == 3 and arg[0] == 'obfuscated':
                    return arg[2]
                else:
                    return arg

            self.fake_command = [obfuscate(c) for c in self.command]

        if env is not None:
            # avoid mutating the original master.cfg dictionary. Each
            # ShellCommand gets its own copy, any start() methods won't be
            # able to modify the original.
            env = env.copy()

        if usePTY == 'slave-config':
            reportDeprecatedWorkerNameUsage(
                "'slave-config' value of 'usePTY' attribute is deprecated, "
                "use None instead.")
            usePTY = None

        args = {
            'workdir': workdir,
            'env': env,
            'want_stdout': want_stdout,
            'want_stderr': want_stderr,
            'logfiles': logfiles,
            'timeout': timeout,
            'maxTime': maxTime,
            'sigtermTime': sigtermTime,
            'usePTY': usePTY,
            'logEnviron': logEnviron,
            'initial_stdin': initialStdin
        }
        if interruptSignal is not None:
            args['interruptSignal'] = interruptSignal
        RemoteCommand.__init__(self,
                               "shell",
                               args,
                               collectStdout=collectStdout,
                               collectStderr=collectStderr,
                               decodeRC=decodeRC,
                               stdioLogName=stdioLogName)
示例#30
0
文件: ec2.py 项目: wallrj/buildbot
    def __init__(self, name, password, instance_type, ami=None,
                 valid_ami_owners=None, valid_ami_location_regex=None,
                 elastic_ip=None, identifier=None, secret_identifier=None,
                 aws_id_file_path=None, user_data=None, region=None,
                 keypair_name=None,
                 security_name=None,
                 spot_instance=False, max_spot_price=1.6, volumes=None,
                 placement=None, price_multiplier=1.2, tags=None,
                 product_description='Linux/UNIX',
                 subnet_id=None, security_group_ids=None, instance_profile_name=None,
                 block_device_map=None, session=None,
                 **kwargs):

        if not boto3:
            config.error("The python module 'boto3' is needed to use a "
                         "EC2LatentWorker")

        if keypair_name is None:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'keypair_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            keypair_name = 'latent_buildbot_slave'
        if security_name is None and not subnet_id:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'security_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            security_name = 'latent_buildbot_slave'

        if volumes is None:
            volumes = []

        if tags is None:
            tags = {}

        AbstractLatentWorker.__init__(self, name, password, **kwargs)

        if security_name and subnet_id:
            raise ValueError(
                'security_name (EC2 classic security groups) is not supported '
                'in a VPC.  Use security_group_ids instead.')
        if not ((ami is not None) ^
                (valid_ami_owners is not None or
                 valid_ami_location_regex is not None)):
            raise ValueError(
                'You must provide either a specific ami, or one or both of '
                'valid_ami_location_regex and valid_ami_owners')
        self.ami = ami
        if valid_ami_owners is not None:
            if isinstance(valid_ami_owners, integer_types):
                valid_ami_owners = (valid_ami_owners,)
            else:
                for element in valid_ami_owners:
                    if not isinstance(element, integer_types):
                        raise ValueError(
                            'valid_ami_owners should be int or iterable '
                            'of ints', element)
        if valid_ami_location_regex is not None:
            if not isinstance(valid_ami_location_regex, string_types):
                raise ValueError(
                    'valid_ami_location_regex should be a string')
            else:
                # verify that regex will compile
                re.compile(valid_ami_location_regex)
        if spot_instance and price_multiplier is None and max_spot_price is None:
            raise ValueError('You must provide either one, or both, of '
                             'price_multiplier or max_spot_price')
        self.valid_ami_owners = None
        if valid_ami_owners:
            self.valid_ami_owners = [str(o) for o in valid_ami_owners]
        self.valid_ami_location_regex = valid_ami_location_regex
        self.instance_type = instance_type
        self.keypair_name = keypair_name
        self.security_name = security_name
        self.user_data = user_data
        self.spot_instance = spot_instance
        self.max_spot_price = max_spot_price
        self.volumes = volumes
        self.price_multiplier = price_multiplier
        self.product_description = product_description

        if None not in [placement, region]:
            self.placement = '%s%s' % (region, placement)
        else:
            self.placement = None
        if identifier is None:
            assert secret_identifier is None, (
                'supply both or neither of identifier, secret_identifier')
            if aws_id_file_path is None:
                home = os.environ['HOME']
                default_path = os.path.join(home, '.ec2', 'aws_id')
                if os.path.exists(default_path):
                    aws_id_file_path = default_path
            if aws_id_file_path:
                log.msg('WARNING: EC2LatentWorker is using deprecated '
                        'aws_id file')
                with open(aws_id_file_path, 'r') as aws_file:
                    identifier = aws_file.readline().strip()
                    secret_identifier = aws_file.readline().strip()
        else:
            assert aws_id_file_path is None, \
                'if you supply the identifier and secret_identifier, ' \
                'do not specify the aws_id_file_path'
            assert secret_identifier is not None, \
                'supply both or neither of identifier, secret_identifier'

        region_found = None

        # Make the EC2 connection.
        self.session = session
        if self.session is None:
            if region is not None:
                for r in boto3.Session(
                        aws_access_key_id=identifier,
                        aws_secret_access_key=secret_identifier).get_available_regions('ec2'):

                    if r == region:
                        region_found = r

                if region_found is not None:
                    self.session = boto3.Session(
                        region_name=region,
                        aws_access_key_id=identifier,
                        aws_secret_access_key=secret_identifier)
                else:
                    raise ValueError(
                        'The specified region does not exist: ' + region)

            else:
                # boto2 defaulted to us-east-1 when region was unset, we
                # mimic this here in boto3
                region = botocore.session.get_session().get_config_variable('region')
                if region is None:
                    region = 'us-east-1'
                self.session = boto3.Session(
                    aws_access_key_id=identifier,
                    aws_secret_access_key=secret_identifier,
                    region_name=region
                )

        self.ec2 = self.session.resource('ec2')

        # Make a keypair
        #
        # We currently discard the keypair data because we don't need it.
        # If we do need it in the future, we will always recreate the keypairs
        # because there is no way to
        # programmatically retrieve the private key component, unless we
        # generate it and store it on the filesystem, which is an unnecessary
        # usage requirement.
        try:
            self.ec2.KeyPair(self.keypair_name).load()
            # key_pair.delete() # would be used to recreate
        except ClientError as e:
            if 'InvalidKeyPair.NotFound' not in str(e):
                if 'AuthFailure' in str(e):
                    log.msg('POSSIBLE CAUSES OF ERROR:\n'
                            '  Did you supply your AWS credentials?\n'
                            '  Did you sign up for EC2?\n'
                            '  Did you put a credit card number in your AWS '
                            'account?\n'
                            'Please doublecheck before reporting a problem.\n')
                raise
            # make one; we would always do this, and stash the result, if we
            # needed the key (for instance, to SSH to the box).  We'd then
            # use paramiko to use the key to connect.
            self.ec2.create_key_pair(KeyName=keypair_name)

        # create security group
        if security_name:
            try:
                self.ec2.SecurityGroup(security_name).load()
            except ClientError as e:
                if 'InvalidGroup.NotFound' in str(e):
                    self.security_group = self.ec2.create_security_group(
                        GroupName=security_name,
                        Description='Authorization to access the buildbot instance.')
                    # Authorize the master as necessary
                    # TODO this is where we'd open the hole to do the reverse pb
                    # connect to the buildbot
                    # ip = urllib.urlopen(
                    #     'http://checkip.amazonaws.com').read().strip()
                    # self.security_group.authorize('tcp', 22, 22, '%s/32' % ip)
                    # self.security_group.authorize('tcp', 80, 80, '%s/32' % ip)
                else:
                    raise

        # get the image
        if self.ami is not None:
            self.image = self.ec2.Image(self.ami)
        else:
            # verify we have access to at least one acceptable image
            discard = self.get_image()
            assert discard

        # get the specified elastic IP, if any
        if elastic_ip is not None:
            # Using ec2.vpc_addresses.filter(PublicIps=[elastic_ip]) throws a
            # NotImplementedError("Filtering not supported in describe_address.") in moto
            # https://github.com/spulec/moto/blob/100ec4e7c8aa3fde87ff6981e2139768816992e4/moto/ec2/responses/elastic_ip_addresses.py#L52
            addresses = self.ec2.meta.client.describe_addresses(
                PublicIps=[elastic_ip])['Addresses']
            if not addresses:
                raise ValueError(
                    'Could not find EIP for IP: ' + elastic_ip)
            allocation_id = addresses[0]['AllocationId']
            elastic_ip = self.ec2.VpcAddress(allocation_id)
        self.elastic_ip = elastic_ip
        self.subnet_id = subnet_id
        self.security_group_ids = security_group_ids
        self.classic_security_groups = [
            self.security_name] if self.security_name else None
        self.instance_profile_name = instance_profile_name
        self.tags = tags
        self.block_device_map = self.create_block_device_mapping(
            block_device_map) if block_device_map else None
示例#31
0
    def load_global(self, filename, config_dict):
        def copy_param(name, alt_key=None,
                       check_type=None, check_type_name=None):
            if name in config_dict:
                v = config_dict[name]
            elif alt_key and alt_key in config_dict:
                v = config_dict[alt_key]
            else:
                return
            if v is not None and check_type and not isinstance(v, check_type):
                error("c['%s'] must be %s" %
                      (name, check_type_name))
            else:
                setattr(self, name, v)

        def copy_int_param(name, alt_key=None):
            copy_param(name, alt_key=alt_key,
                       check_type=int, check_type_name='an int')

        def copy_str_param(name, alt_key=None):
            copy_param(name, alt_key=alt_key,
                       check_type=string_types, check_type_name='a string')

        copy_str_param('title', alt_key='projectName')
        copy_str_param('titleURL', alt_key='projectURL')
        copy_str_param('buildbotURL')

        copy_int_param('changeHorizon')
        copy_int_param('logHorizon')
        copy_int_param('buildHorizon')

        if 'eventHorizon' in config_dict:
            warnDeprecated(
                '0.9.0',
                '`eventHorizon` is deprecated and will be removed in a future version.',
            )

        copy_int_param('logCompressionLimit')

        self.logCompressionMethod = config_dict.get(
            'logCompressionMethod', 'gz')
        if self.logCompressionMethod not in ('raw', 'bz2', 'gz', 'lz4'):
            error(
                "c['logCompressionMethod'] must be 'raw', 'bz2', 'gz' or 'lz4'")

        if self.logCompressionMethod == "lz4":
            try:

                import lz4
                [lz4]
            except ImportError:
                error(
                    "To set c['logCompressionMethod'] to 'lz4' you must install the lz4 library ('pip install lz4')")

        copy_int_param('logMaxSize')
        copy_int_param('logMaxTailSize')
        copy_param('logEncoding')

        properties = config_dict.get('properties', {})
        if not isinstance(properties, dict):
            error("c['properties'] must be a dictionary")
        else:
            self.properties.update(properties, filename)

        collapseRequests = config_dict.get('collapseRequests')
        if (collapseRequests not in (None, True, False)
                and not callable(collapseRequests)):
            error("collapseRequests must be a callable, True, or False")
        else:
            self.collapseRequests = collapseRequests

        codebaseGenerator = config_dict.get('codebaseGenerator')
        if (codebaseGenerator is not None and
                not callable(codebaseGenerator)):
            error(
                "codebaseGenerator must be a callable accepting a dict and returning a str")
        else:
            self.codebaseGenerator = codebaseGenerator

        prioritizeBuilders = config_dict.get('prioritizeBuilders')
        if prioritizeBuilders is not None and not callable(prioritizeBuilders):
            error("prioritizeBuilders must be a callable")
        else:
            self.prioritizeBuilders = prioritizeBuilders

        protocols = config_dict.get('protocols', {})
        if isinstance(protocols, dict):
            for proto, options in iteritems(protocols):
                if not isinstance(proto, str):
                    error("c['protocols'] keys must be strings")
                if not isinstance(options, dict):
                    error("c['protocols']['%s'] must be a dict" % proto)
                    return
                if (proto == "pb" and options.get("port") and
                        'slavePortnum' in config_dict):
                    error("Both c['slavePortnum'] and c['protocols']['pb']['port']"
                          " defined, recommended to remove slavePortnum and leave"
                          " only c['protocols']['pb']['port']")
                if proto == "wamp":
                    self.check_wamp_proto(options)
        else:
            error("c['protocols'] must be dict")
            return
        self.protocols = protocols

        # saved for backward compatability
        if 'slavePortnum' in config_dict:
            reportDeprecatedWorkerNameUsage(
                "c['slavePortnum'] key is deprecated, use "
                "c['protocols']['pb']['port'] instead",
                filename=filename)
            port = config_dict.get('slavePortnum')
            if isinstance(port, int):
                port = "tcp:%d" % port
            pb_options = self.protocols.get('pb', {})
            pb_options['port'] = port
            self.protocols['pb'] = pb_options

        if 'multiMaster' in config_dict:
            self.multiMaster = config_dict["multiMaster"]

        if 'debugPassword' in config_dict:
            log.msg(
                "the 'debugPassword' parameter is unused and can be removed from the configuration flie")

        if 'manhole' in config_dict:
            # we don't check that this is a manhole instance, since that
            # requires importing buildbot.manhole for every user, and currently
            # that will fail if pycrypto isn't installed
            self.manhole = config_dict['manhole']

        if 'revlink' in config_dict:
            revlink = config_dict['revlink']
            if not callable(revlink):
                error("revlink must be a callable")
            else:
                self.revlink = revlink
示例#32
0
    def __init__(self, name=None, workername=None, workernames=None,
                 builddir=None, workerbuilddir=None, factory=None,
                 tags=None, category=None,
                 nextWorker=None, nextBuild=None, locks=None, env=None,
                 properties=None, collapseRequests=None, description=None,
                 canStartBuild=None,

                 slavename=None,  # deprecated, use `workername` instead
                 slavenames=None,  # deprecated, use `workernames` instead
                 # deprecated, use `workerbuilddir` instead
                 slavebuilddir=None,
                 nextSlave=None,  # deprecated, use `nextWorker` instead
                 ):

        # Deprecated API support.
        if slavename is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavename' keyword argument is deprecated, "
                "use 'workername' instead")
            assert workername is None
            workername = slavename
        if slavenames is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavenames' keyword argument is deprecated, "
                "use 'workernames' instead")
            assert workernames is None
            workernames = slavenames
        if slavebuilddir is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavebuilddir' keyword argument is deprecated, "
                "use 'workerbuilddir' instead")
            assert workerbuilddir is None
            workerbuilddir = slavebuilddir
        if nextSlave is not None:
            reportDeprecatedWorkerNameUsage(
                "'nextSlave' keyword argument is deprecated, "
                "use 'nextWorker' instead")
            assert nextWorker is None
            nextWorker = nextSlave

        # name is required, and can't start with '_'
        if not name or type(name) not in (bytes, text_type):
            error("builder's name is required")
            name = '<unknown>'
        elif name[0] == '_' and name not in RESERVED_UNDERSCORE_NAMES:
            error(
                "builder names must not start with an underscore: '%s'" % name)
        try:
            self.name = util.ascii2unicode(name)
        except UnicodeDecodeError:
            error("builder names must be unicode or ASCII")

        # factory is required
        if factory is None:
            error("builder '%s' has no factory" % name)
        from buildbot.process.factory import BuildFactory
        if factory is not None and not isinstance(factory, BuildFactory):
            error("builder '%s's factory is not a BuildFactory instance" %
                  name)
        self.factory = factory

        # workernames can be a single worker name or a list, and should also
        # include workername, if given
        if isinstance(workernames, str):
            workernames = [workernames]
        if workernames:
            if not isinstance(workernames, list):
                error("builder '%s': workernames must be a list or a string" %
                      (name,))
        else:
            workernames = []

        if workername:
            if not isinstance(workername, str):
                error("builder '%s': workername must be a string but it is %r" % (name, workername))
            workernames = workernames + [workername]
        if not workernames:
            error("builder '%s': at least one workername is required" %
                  (name,))

        self.workernames = workernames
        self._registerOldWorkerAttr("workernames")

        # builddir defaults to name
        if builddir is None:
            builddir = safeTranslate(name)
            builddir = bytes2NativeString(builddir)
        self.builddir = builddir

        # workerbuilddir defaults to builddir
        if workerbuilddir is None:
            workerbuilddir = builddir
        self.workerbuilddir = workerbuilddir
        self._registerOldWorkerAttr("workerbuilddir")

        # remainder are optional

        if category and tags:
            error("builder '%s': builder categories are deprecated and "
                  "replaced by tags; you should only specify tags" % (name,))
        if category:
            warnDeprecated("0.9", "builder '%s': builder categories are "
                                  "deprecated and should be replaced with "
                                  "'tags=[cat]'" % (name,))
            if not isinstance(category, str):
                error("builder '%s': category must be a string" % (name,))
            tags = [category]
        if tags:
            if not isinstance(tags, list):
                error("builder '%s': tags must be a list" % (name,))
            bad_tags = any((tag for tag in tags if not isinstance(tag, str)))
            if bad_tags:
                error(
                    "builder '%s': tags list contains something that is not a string" % (name,))

            if len(tags) != len(set(tags)):
                dupes = " ".join(set([x for x in tags if tags.count(x) > 1]))
                error(
                    "builder '%s': tags list contains duplicate tags: %s" % (name, dupes))
        else:
            tags = []

        self.tags = tags

        self.nextWorker = nextWorker
        self._registerOldWorkerAttr("nextWorker")
        if nextWorker and not callable(nextWorker):
            error('nextWorker must be a callable')
        # Keeping support of the previous nextWorker API
        if nextWorker:
            argCount = self._countFuncArgs(nextWorker)
            if (argCount == 2 or (isinstance(nextWorker, MethodType) and
                                  argCount == 3)):
                warnDeprecated(
                    "0.9", "nextWorker now takes a "
                    "3rd argument (build request)")
                self.nextWorker = lambda x, y, z: nextWorker(
                    x, y)  # pragma: no cover
        self.nextBuild = nextBuild
        if nextBuild and not callable(nextBuild):
            error('nextBuild must be a callable')
        self.canStartBuild = canStartBuild
        if canStartBuild and not callable(canStartBuild):
            error('canStartBuild must be a callable')

        self.locks = locks or []
        self.env = env or {}
        if not isinstance(self.env, dict):
            error("builder's env must be a dictionary")
        self.properties = properties or {}
        self.collapseRequests = collapseRequests

        self.description = description
示例#33
0
    def load_global(self, filename, config_dict):
        def copy_param(name,
                       alt_key=None,
                       check_type=None,
                       check_type_name=None,
                       can_be_callable=False):
            if name in config_dict:
                v = config_dict[name]
            elif alt_key and alt_key in config_dict:
                v = config_dict[alt_key]
            else:
                return
            if v is not None and check_type and not (isinstance(
                    v, check_type) or (can_be_callable and callable(v))):
                error("c['%s'] must be %s" % (name, check_type_name))
            else:
                setattr(self, name, v)

        def copy_int_param(name, alt_key=None):
            copy_param(name,
                       alt_key=alt_key,
                       check_type=int,
                       check_type_name='an int')

        def copy_str_param(name, alt_key=None):
            copy_param(name,
                       alt_key=alt_key,
                       check_type=string_types,
                       check_type_name='a string')

        copy_str_param('title', alt_key='projectName')
        copy_str_param('titleURL', alt_key='projectURL')
        copy_str_param('buildbotURL')

        def copy_str_or_callable_param(name, alt_key=None):
            copy_param(name,
                       alt_key=alt_key,
                       check_type=string_types,
                       check_type_name='a string or callable',
                       can_be_callable=True)

        if "buildbotNetUsageData" not in config_dict:
            warnDeprecated(
                '0.9.0',
                '`buildbotNetUsageData` is not configured and defaults to basic\n'
                'This parameter helps the buildbot development team to understand the installation base\n'
                'No personal information is collected.\n'
                'Only installation software version info and plugin usage is sent\n'
                'You can `opt-out` by setting this variable to None\n'
                'Or `opt-in` for more information by setting it to "full"\n')
        copy_str_or_callable_param('buildbotNetUsageData')

        copy_int_param('changeHorizon')
        copy_int_param('logHorizon')
        copy_int_param('buildHorizon')

        if 'eventHorizon' in config_dict:
            warnDeprecated(
                '0.9.0',
                '`eventHorizon` is deprecated and will be removed in a future version.',
            )

        copy_int_param('logCompressionLimit')

        self.logCompressionMethod = config_dict.get('logCompressionMethod',
                                                    'gz')
        if self.logCompressionMethod not in ('raw', 'bz2', 'gz', 'lz4'):
            error(
                "c['logCompressionMethod'] must be 'raw', 'bz2', 'gz' or 'lz4'"
            )

        if self.logCompressionMethod == "lz4":
            try:

                import lz4
                [lz4]
            except ImportError:
                error(
                    "To set c['logCompressionMethod'] to 'lz4' you must install the lz4 library ('pip install lz4')"
                )

        copy_int_param('logMaxSize')
        copy_int_param('logMaxTailSize')
        copy_param('logEncoding')

        properties = config_dict.get('properties', {})
        if not isinstance(properties, dict):
            error("c['properties'] must be a dictionary")
        else:
            self.properties.update(properties, filename)

        collapseRequests = config_dict.get('collapseRequests')
        if (collapseRequests not in (None, True, False)
                and not callable(collapseRequests)):
            error("collapseRequests must be a callable, True, or False")
        else:
            self.collapseRequests = collapseRequests

        codebaseGenerator = config_dict.get('codebaseGenerator')
        if (codebaseGenerator is not None and not callable(codebaseGenerator)):
            error(
                "codebaseGenerator must be a callable accepting a dict and returning a str"
            )
        else:
            self.codebaseGenerator = codebaseGenerator

        prioritizeBuilders = config_dict.get('prioritizeBuilders')
        if prioritizeBuilders is not None and not callable(prioritizeBuilders):
            error("prioritizeBuilders must be a callable")
        else:
            self.prioritizeBuilders = prioritizeBuilders

        protocols = config_dict.get('protocols', {})
        if isinstance(protocols, dict):
            for proto, options in iteritems(protocols):
                if not isinstance(proto, str):
                    error("c['protocols'] keys must be strings")
                if not isinstance(options, dict):
                    error("c['protocols']['%s'] must be a dict" % proto)
                    return
                if (proto == "pb" and options.get("port")
                        and 'slavePortnum' in config_dict):
                    error(
                        "Both c['slavePortnum'] and c['protocols']['pb']['port']"
                        " defined, recommended to remove slavePortnum and leave"
                        " only c['protocols']['pb']['port']")
                if proto == "wamp":
                    self.check_wamp_proto(options)
        else:
            error("c['protocols'] must be dict")
            return
        self.protocols = protocols

        # saved for backward compatability
        if 'slavePortnum' in config_dict:
            reportDeprecatedWorkerNameUsage(
                "c['slavePortnum'] key is deprecated, use "
                "c['protocols']['pb']['port'] instead",
                filename=filename)
            port = config_dict.get('slavePortnum')
            if isinstance(port, int):
                port = "tcp:%d" % port
            pb_options = self.protocols.get('pb', {})
            pb_options['port'] = port
            self.protocols['pb'] = pb_options

        if 'multiMaster' in config_dict:
            self.multiMaster = config_dict["multiMaster"]

        if 'debugPassword' in config_dict:
            log.msg(
                "the 'debugPassword' parameter is unused and can be removed from the configuration flie"
            )

        if 'manhole' in config_dict:
            # we don't check that this is a manhole instance, since that
            # requires importing buildbot.manhole for every user, and currently
            # that will fail if pycrypto isn't installed
            self.manhole = config_dict['manhole']

        if 'revlink' in config_dict:
            revlink = config_dict['revlink']
            if not callable(revlink):
                error("revlink must be a callable")
            else:
                self.revlink = revlink
示例#34
0
    def load_global(self, filename, config_dict):
        def copy_param(name, alt_key=None,
                       check_type=None, check_type_name=None, can_be_callable=False):
            if name in config_dict:
                v = config_dict[name]
            elif alt_key and alt_key in config_dict:
                v = config_dict[alt_key]
            else:
                return
            if v is not None and check_type and not (
                    isinstance(v, check_type) or (can_be_callable and callable(v))):
                error("c['%s'] must be %s" %
                      (name, check_type_name))
            else:
                setattr(self, name, v)

        def copy_int_param(name, alt_key=None):
            copy_param(name, alt_key=alt_key,
                       check_type=int, check_type_name='an int')

        def copy_str_param(name, alt_key=None):
            copy_param(name, alt_key=alt_key,
                       check_type=string_types, check_type_name='a string')

        copy_str_param('title', alt_key='projectName')
        copy_str_param('titleURL', alt_key='projectURL')
        copy_str_param('buildbotURL')

        def copy_str_or_callable_param(name, alt_key=None):
            copy_param(name, alt_key=alt_key,
                       check_type=string_types, check_type_name='a string or callable', can_be_callable=True)

        if "buildbotNetUsageData" not in config_dict:
            if _in_unit_tests:
                self.buildbotNetUsageData = None
            else:
                warnDeprecated(
                    '0.9.0',
                    '`buildbotNetUsageData` is not configured and defaults to basic.\n'
                    'This parameter helps the buildbot development team to understand'
                    ' the installation base.\n'
                    'No personal information is collected.\n'
                    'Only installation software version info and plugin usage is sent.\n'
                    'You can `opt-out` by setting this variable to None.\n'
                    'Or `opt-in` for more information by setting it to "full".\n'
                )
        copy_str_or_callable_param('buildbotNetUsageData')

        for horizon in ('logHorizon', 'buildHorizon', 'eventHorizon'):
            if horizon in config_dict:
                warnDeprecated(
                    '0.9.0',
                    "NOTE: `{}` is deprecated and ignored "
                    "They are replaced by util.JanitorConfigurator".format(horizon))

        copy_int_param('changeHorizon')
        copy_int_param('logCompressionLimit')

        self.logCompressionMethod = config_dict.get(
            'logCompressionMethod', 'gz')
        if self.logCompressionMethod not in ('raw', 'bz2', 'gz', 'lz4'):
            error(
                "c['logCompressionMethod'] must be 'raw', 'bz2', 'gz' or 'lz4'")

        if self.logCompressionMethod == "lz4":
            try:

                import lz4
                [lz4]
            except ImportError:
                error(
                    "To set c['logCompressionMethod'] to 'lz4' you must install the lz4 library ('pip install lz4')")

        copy_int_param('logMaxSize')
        copy_int_param('logMaxTailSize')
        copy_param('logEncoding')

        properties = config_dict.get('properties', {})
        if not isinstance(properties, dict):
            error("c['properties'] must be a dictionary")
        else:
            self.properties.update(properties, filename)

        collapseRequests = config_dict.get('collapseRequests')
        if (collapseRequests not in (None, True, False)
                and not callable(collapseRequests)):
            error("collapseRequests must be a callable, True, or False")
        else:
            self.collapseRequests = collapseRequests

        codebaseGenerator = config_dict.get('codebaseGenerator')
        if (codebaseGenerator is not None and
                not callable(codebaseGenerator)):
            error(
                "codebaseGenerator must be a callable accepting a dict and returning a str")
        else:
            self.codebaseGenerator = codebaseGenerator

        prioritizeBuilders = config_dict.get('prioritizeBuilders')
        if prioritizeBuilders is not None and not callable(prioritizeBuilders):
            error("prioritizeBuilders must be a callable")
        else:
            self.prioritizeBuilders = prioritizeBuilders

        protocols = config_dict.get('protocols', {})
        if isinstance(protocols, dict):
            for proto, options in iteritems(protocols):
                if not isinstance(proto, str):
                    error("c['protocols'] keys must be strings")
                if not isinstance(options, dict):
                    error("c['protocols']['%s'] must be a dict" % proto)
                    return
                if (proto == "pb" and options.get("port") and
                        'slavePortnum' in config_dict):
                    error("Both c['slavePortnum'] and c['protocols']['pb']['port']"
                          " defined, recommended to remove slavePortnum and leave"
                          " only c['protocols']['pb']['port']")
                if proto == "wamp":
                    self.check_wamp_proto(options)
        else:
            error("c['protocols'] must be dict")
            return
        self.protocols = protocols

        # saved for backward compatibility
        if 'slavePortnum' in config_dict:
            reportDeprecatedWorkerNameUsage(
                "c['slavePortnum'] key is deprecated, use "
                "c['protocols']['pb']['port'] instead",
                filename=filename)
            port = config_dict.get('slavePortnum')
            if isinstance(port, int):
                port = "tcp:%d" % port
            pb_options = self.protocols.get('pb', {})
            pb_options['port'] = port
            self.protocols['pb'] = pb_options

        if 'multiMaster' in config_dict:
            self.multiMaster = config_dict["multiMaster"]

        if 'debugPassword' in config_dict:
            log.msg(
                "the 'debugPassword' parameter is unused and can be removed from the configuration flie")

        if 'manhole' in config_dict:
            # we don't check that this is a manhole instance, since that
            # requires importing buildbot.manhole for every user, and currently
            # that will fail if cryptography isn't installed
            self.manhole = config_dict['manhole']

        if 'revlink' in config_dict:
            revlink = config_dict['revlink']
            if not callable(revlink):
                error("revlink must be a callable")
            else:
                self.revlink = revlink
示例#35
0
    def __init__(self,
                 name,
                 password,
                 instance_type,
                 ami=None,
                 valid_ami_owners=None,
                 valid_ami_location_regex=None,
                 elastic_ip=None,
                 identifier=None,
                 secret_identifier=None,
                 aws_id_file_path=None,
                 user_data=None,
                 region=None,
                 keypair_name=None,
                 security_name=None,
                 spot_instance=False,
                 max_spot_price=1.6,
                 volumes=None,
                 placement=None,
                 price_multiplier=1.2,
                 tags=None,
                 retry=1,
                 retry_price_adjustment=1,
                 product_description='Linux/UNIX',
                 subnet_id=None,
                 security_group_ids=None,
                 instance_profile_name=None,
                 block_device_map=None,
                 **kwargs):

        if not boto:
            config.error("The python module 'boto' is needed to use a "
                         "EC2LatentWorker")

        if keypair_name is None:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'keypair_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            keypair_name = 'latent_buildbot_slave'
        if security_name is None and not subnet_id:
            reportDeprecatedWorkerNameUsage(
                "Use of default value of 'security_name' of EC2LatentWorker "
                "constructor is deprecated. Please explicitly specify value")
            security_name = 'latent_buildbot_slave'

        if volumes is None:
            volumes = []

        if tags is None:
            tags = {}

        AbstractLatentWorker.__init__(self, name, password, **kwargs)

        if security_name and subnet_id:
            raise ValueError(
                'security_name (EC2 classic security groups) is not supported '
                'in a VPC.  Use security_group_ids instead.')
        if not ((ami is not None) ^ (valid_ami_owners is not None
                                     or valid_ami_location_regex is not None)):
            raise ValueError(
                'You must provide either a specific ami, or one or both of '
                'valid_ami_location_regex and valid_ami_owners')
        self.ami = ami
        if valid_ami_owners is not None:
            if isinstance(valid_ami_owners, (int, long)):
                valid_ami_owners = (valid_ami_owners, )
            else:
                for element in valid_ami_owners:
                    if not isinstance(element, (int, long)):
                        raise ValueError(
                            'valid_ami_owners should be int or iterable '
                            'of ints', element)
        if valid_ami_location_regex is not None:
            if not isinstance(valid_ami_location_regex, basestring):
                raise ValueError('valid_ami_location_regex should be a string')
            else:
                # verify that regex will compile
                re.compile(valid_ami_location_regex)
        self.valid_ami_owners = valid_ami_owners
        self.valid_ami_location_regex = valid_ami_location_regex
        self.instance_type = instance_type
        self.keypair_name = keypair_name
        self.security_name = security_name
        self.user_data = user_data
        self.spot_instance = spot_instance
        self.max_spot_price = max_spot_price
        self.volumes = volumes
        self.price_multiplier = price_multiplier
        self.retry_price_adjustment = retry_price_adjustment
        self.retry = retry
        self.attempt = 1
        self.product_description = product_description

        if None not in [placement, region]:
            self.placement = '%s%s' % (region, placement)
        else:
            self.placement = None
        if identifier is None:
            assert secret_identifier is None, (
                'supply both or neither of identifier, secret_identifier')
            if aws_id_file_path is None:
                home = os.environ['HOME']
                default_path = os.path.join(home, '.ec2', 'aws_id')
                if os.path.exists(default_path):
                    aws_id_file_path = default_path
            if aws_id_file_path:
                log.msg('WARNING: EC2LatentWorker is using deprecated '
                        'aws_id file')
                with open(aws_id_file_path, 'r') as aws_file:
                    identifier = aws_file.readline().strip()
                    secret_identifier = aws_file.readline().strip()
        else:
            assert aws_id_file_path is None, \
                'if you supply the identifier and secret_identifier, ' \
                'do not specify the aws_id_file_path'
            assert secret_identifier is not None, \
                'supply both or neither of identifier, secret_identifier'

        region_found = None

        # Make the EC2 connection.
        if region is not None:
            for r in boto.ec2.regions(aws_access_key_id=identifier,
                                      aws_secret_access_key=secret_identifier):

                if r.name == region:
                    region_found = r

            if region_found is not None:
                self.ec2_conn = boto.ec2.connect_to_region(
                    region,
                    aws_access_key_id=identifier,
                    aws_secret_access_key=secret_identifier)
            else:
                raise ValueError('The specified region does not exist: ' +
                                 region)

        else:
            self.ec2_conn = boto.connect_ec2(identifier, secret_identifier)

        # Make a keypair
        #
        # We currently discard the keypair data because we don't need it.
        # If we do need it in the future, we will always recreate the keypairs
        # because there is no way to
        # programmatically retrieve the private key component, unless we
        # generate it and store it on the filesystem, which is an unnecessary
        # usage requirement.
        try:
            key_pair = self.ec2_conn.get_all_key_pairs(keypair_name)[0]
            assert key_pair
            # key_pair.delete() # would be used to recreate
        except boto.exception.EC2ResponseError as e:
            if 'InvalidKeyPair.NotFound' not in e.body:
                if 'AuthFailure' in e.body:
                    log.msg('POSSIBLE CAUSES OF ERROR:\n'
                            '  Did you supply your AWS credentials?\n'
                            '  Did you sign up for EC2?\n'
                            '  Did you put a credit card number in your AWS '
                            'account?\n'
                            'Please doublecheck before reporting a problem.\n')
                raise
            # make one; we would always do this, and stash the result, if we
            # needed the key (for instance, to SSH to the box).  We'd then
            # use paramiko to use the key to connect.
            self.ec2_conn.create_key_pair(keypair_name)

        # create security group
        if security_name:
            try:
                group = self.ec2_conn.get_all_security_groups(security_name)[0]
                assert group
            except boto.exception.EC2ResponseError as e:
                if 'InvalidGroup.NotFound' in e.body:
                    self.security_group = self.ec2_conn.create_security_group(
                        security_name,
                        'Authorization to access the buildbot instance.')
                    # Authorize the master as necessary
                    # TODO this is where we'd open the hole to do the reverse pb
                    # connect to the buildbot
                    # ip = urllib.urlopen(
                    #     'http://checkip.amazonaws.com').read().strip()
                    # self.security_group.authorize('tcp', 22, 22, '%s/32' % ip)
                    # self.security_group.authorize('tcp', 80, 80, '%s/32' % ip)
                else:
                    raise

        # get the image
        if self.ami is not None:
            self.image = self.ec2_conn.get_image(self.ami)
        else:
            # verify we have access to at least one acceptable image
            discard = self.get_image()
            assert discard

        # get the specified elastic IP, if any
        if elastic_ip is not None:
            elastic_ip = self.ec2_conn.get_all_addresses([elastic_ip])[0]
        self.elastic_ip = elastic_ip
        self.subnet_id = subnet_id
        self.security_group_ids = security_group_ids
        self.classic_security_groups = [self.security_name
                                        ] if self.security_name else None
        self.instance_profile_name = instance_profile_name
        self.tags = tags
        self.block_device_map = self.create_block_device_mapping(
            block_device_map)
示例#36
0
    def __init__(
            self,
            name=None,
            workername=None,
            workernames=None,
            builddir=None,
            workerbuilddir=None,
            factory=None,
            tags=None,
            category=None,
            nextWorker=None,
            nextBuild=None,
            locks=None,
            env=None,
            properties=None,
            collapseRequests=None,
            description=None,
            canStartBuild=None,
            slavename=None,  # deprecated, use `workername` instead
            slavenames=None,  # deprecated, use `workernames` instead
            # deprecated, use `workerbuilddir` instead
        slavebuilddir=None,
            nextSlave=None,  # deprecated, use `nextWorker` instead
    ):

        # Deprecated API support.
        if slavename is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavename' keyword argument is deprecated, "
                "use 'workername' instead")
            assert workername is None
            workername = slavename
        if slavenames is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavenames' keyword argument is deprecated, "
                "use 'workernames' instead")
            assert workernames is None
            workernames = slavenames
        if slavebuilddir is not None:
            reportDeprecatedWorkerNameUsage(
                "'slavebuilddir' keyword argument is deprecated, "
                "use 'workerbuilddir' instead")
            assert workerbuilddir is None
            workerbuilddir = slavebuilddir
        if nextSlave is not None:
            reportDeprecatedWorkerNameUsage(
                "'nextSlave' keyword argument is deprecated, "
                "use 'nextWorker' instead")
            assert nextWorker is None
            nextWorker = nextSlave

        # name is required, and can't start with '_'
        if not name or type(name) not in (str, text_type):
            error("builder's name is required")
            name = '<unknown>'
        elif name[0] == '_':
            error("builder names must not start with an underscore: '%s'" %
                  name)
        try:
            self.name = util.ascii2unicode(name)
        except UnicodeDecodeError:
            error("builder names must be unicode or ASCII")

        # factory is required
        if factory is None:
            error("builder '%s' has no factory" % name)
        from buildbot.process.factory import BuildFactory
        if factory is not None and not isinstance(factory, BuildFactory):
            error("builder '%s's factory is not a BuildFactory instance" %
                  name)
        self.factory = factory

        # workernames can be a single worker name or a list, and should also
        # include workername, if given
        if isinstance(workernames, str):
            workernames = [workernames]
        if workernames:
            if not isinstance(workernames, list):
                error("builder '%s': workernames must be a list or a string" %
                      (name, ))
        else:
            workernames = []

        if workername:
            if not isinstance(workername, str):
                error("builder '%s': workername must be a string" % (name, ))
            workernames = workernames + [workername]
        if not workernames:
            error("builder '%s': at least one workername is required" %
                  (name, ))

        self.workernames = workernames
        self._registerOldWorkerAttr("workernames")

        # builddir defaults to name
        if builddir is None:
            builddir = safeTranslate(name)
        self.builddir = builddir

        # workerbuilddir defaults to builddir
        if workerbuilddir is None:
            workerbuilddir = builddir
        self.workerbuilddir = workerbuilddir
        self._registerOldWorkerAttr("workerbuilddir")

        # remainder are optional

        if category and tags:
            error("builder '%s': builder categories are deprecated and "
                  "replaced by tags; you should only specify tags" % (name, ))
        if category:
            warnDeprecated(
                "0.9", "builder '%s': builder categories are "
                "deprecated and should be replaced with "
                "'tags=[cat]'" % (name, ))
            if not isinstance(category, str):
                error("builder '%s': category must be a string" % (name, ))
            tags = [category]
        if tags:
            if not isinstance(tags, list):
                error("builder '%s': tags must be a list" % (name, ))
            bad_tags = any((tag for tag in tags if not isinstance(tag, str)))
            if bad_tags:
                error(
                    "builder '%s': tags list contains something that is not a string"
                    % (name, ))

            if len(tags) != len(set(tags)):
                dupes = " ".join(set([x for x in tags if tags.count(x) > 1]))
                error("builder '%s': tags list contains duplicate tags: %s" %
                      (name, dupes))
        else:
            tags = []

        self.tags = tags

        self.nextWorker = nextWorker
        self._registerOldWorkerAttr("nextWorker")
        if nextWorker and not callable(nextWorker):
            error('nextWorker must be a callable')
            # Keeping support of the previous nextWorker API
        if nextWorker and (nextWorker.func_code.co_argcount == 2 or
                           (isinstance(nextWorker, MethodType)
                            and nextWorker.func_code.co_argcount == 3)):
            warnDeprecated(
                "0.9", "nextWorker now takes a 3rd argument (build request)")
            self.nextWorker = lambda x, y, z: nextWorker(x, y
                                                         )  # pragma: no cover
        self.nextBuild = nextBuild
        if nextBuild and not callable(nextBuild):
            error('nextBuild must be a callable')
        self.canStartBuild = canStartBuild
        if canStartBuild and not callable(canStartBuild):
            error('canStartBuild must be a callable')

        self.locks = locks or []
        self.env = env or {}
        if not isinstance(self.env, dict):
            error("builder's env must be a dictionary")
        self.properties = properties or {}
        self.collapseRequests = collapseRequests

        self.description = description