Ejemplo n.º 1
0
def test_invalid_choice(application_name):
    instance_management = InstanceManagement(
        huskar_client, application_name, 'config')
    with raises(AssertionError):
        InstanceManagement(huskar_client, application_name, 'woo')
    with raises(AssertionError):
        instance_management.get_service_info()
    with raises(AssertionError):
        instance_management.get_cluster_info('any')
Ejemplo n.º 2
0
 def _get_cluster_info(self, application_name, cluster_name):
     im = InstanceManagement(huskar_client, application_name,
                             SERVICE_SUBDOMAIN)
     try:
         return im.get_cluster_info(cluster_name)
     except MalformedDataError as e:
         logger.warning('Failed to parse symlink "%s"', e.info.path)
         return e.info
Ejemplo n.º 3
0
class InstanceFacade(object):
    client = huskar_client

    def __init__(self, subdomain, application_name, include_comment=True):
        self.subdomain = subdomain
        self.application_name = application_name
        self.include_comment = include_comment
        self.im = InstanceManagement(self.client, application_name, subdomain)
        self.im.set_context(g.application_name, g.cluster_name)

    def fetch_instance_list(self, pairs, resolve=True):
        include_comment = self.include_comment and not g.auth.is_minimal_mode
        for cluster_name, key in pairs:
            info, physical_name = self.im.get_instance(cluster_name,
                                                       key,
                                                       resolve=resolve)
            if info.stat is None:
                continue
            data = {
                'application': self.application_name,
                'cluster': cluster_name,
                'key': key,
                'value': info.data,
                'meta': self.make_meta_info(info),
            }
            if self.subdomain == SERVICE_SUBDOMAIN:
                data['runtime'] = self.make_runtime_field(info)
                if physical_name:
                    data['cluster_physical_name'] = physical_name
            if include_comment:
                comment = get_comment(self.application_name, cluster_name,
                                      self.subdomain, key)
                data['comment'] = comment
            yield data

    @retry(OutOfSyncError, interval=0.5, max_retry=3)
    def set_instance(self,
                     cluster_name,
                     key,
                     value,
                     comment=None,
                     overwrite=False):
        instance, _ = self.im.get_instance(cluster_name, key, resolve=False)
        if overwrite or instance.stat is None:
            instance.data = value
            instance.save()
            if self.include_comment and comment is not None:
                set_comment(self.application_name, cluster_name,
                            self.subdomain, key, comment)
            return instance

    def get_instance(self, cluster_name, key, resolve=True):
        iterator = self.fetch_instance_list([(cluster_name, key)],
                                            resolve=resolve)
        return next(iterator, None)

    def get_instance_list(self, resolve=True):
        pairs = ((cluster_name, key)
                 for cluster_name in self.im.list_cluster_names()
                 for key in self.im.list_instance_keys(cluster_name,
                                                       resolve=resolve))
        return list(self.fetch_instance_list(pairs, resolve=resolve))

    def get_instance_list_by_cluster(self, cluster_name, resolve=True):
        keys = self.im.list_instance_keys(cluster_name, resolve=resolve)
        pairs = ((cluster_name, k) for k in keys)
        return list(self.fetch_instance_list(pairs, resolve=resolve))

    def get_merged_instance_list(self, cluster_name):
        overall_instance_list = self.get_instance_list_by_cluster(OVERALL)
        current_instance_list = self.get_instance_list_by_cluster(cluster_name)
        return merge_instance_list(self.application_name,
                                   overall_instance_list,
                                   current_instance_list, cluster_name)

    def get_cluster_list(self):
        cluster_names = self.im.list_cluster_names()
        for cluster_name in cluster_names:
            cluster_info = None
            if self.subdomain == SERVICE_SUBDOMAIN:
                try:
                    cluster_info = self.im.get_cluster_info(cluster_name)
                    meta = self.make_meta_info(cluster_info, is_cluster=True)
                except MalformedDataError as e:
                    logger.warning('Failed to parse info "%s"', e.info.path)
                    meta = self.make_meta_info(e.info, is_cluster=True)
                if cluster_info and cluster_info.data:
                    route_map = cluster_info.get_route()
                    yield {
                        'name': cluster_name,
                        'physical_name': cluster_info.get_link(),
                        'route': sorted(self.make_route_list(route_map)),
                        'meta': meta
                    }
                else:
                    yield {
                        'name': cluster_name,
                        'physical_name': None,
                        'route': [],
                        'meta': meta
                    }
            else:
                yield {'name': cluster_name}

    @classmethod
    def check_instance_key(cls, subdomain, application_name, cluster_name,
                           key):
        if (subdomain == CONFIG_SUBDOMAIN
                and key in INFRA_CONFIG_KEYS.values()):
            abort(400, 'The key {key} is reserved.'.format(key=key))

        cls.check_instance_key_in_creation(subdomain, application_name,
                                           cluster_name, key)

    @classmethod
    def check_instance_key_in_creation(cls, subdomain, application_name,
                                       cluster_name, key):
        if subdomain != CONFIG_SUBDOMAIN:
            return
        if not switch.is_switched_on(SWITCH_ENABLE_CONFIG_PREFIX_BLACKLIST,
                                     False):
            return
        if config_facade.exists(application_name, cluster_name, key=key):
            return

        for prefix in settings.CONFIG_PREFIX_BLACKLIST:
            if key.startswith(prefix):
                abort(
                    400,
                    'The key {key} starts with {prefix} is denied.'.format(
                        key=key, prefix=prefix))

    @classmethod
    def make_route_list(cls, route_map):
        for route_key, cluster_name in route_map.iteritems():
            route_key = parse_route_key(route_key)
            yield {
                'application_name': route_key.application_name,
                'intent': route_key.intent,
                'cluster_name': cluster_name
            }

    @classmethod
    def make_runtime_field(cls, info):
        try:
            value = json.loads(info.data)
        except (TypeError, ValueError):
            logger.warning('Failed to parse %r', info.path)
            return
        if not isinstance(value, dict):
            logger.warning('Unexpected schema of %r', info.path)
            return
        state = value.get('state') or None
        return state and json.dumps({'state': state})

    @classmethod
    def make_meta_info(cls, info, is_cluster=False):
        meta = {
            'last_modified': int(info.stat.last_modified * 1000),
            'created': int(info.stat.created * 1000),
            'version': info.stat.version,
        }
        if is_cluster:
            if info.get_link() is None:
                meta['instance_count'] = info.stat.children_count
            else:
                meta['is_symbol_only'] = info.stat.children_count == 0
        return meta