예제 #1
0
    async def store_stock(self, ctx, store_name: str, item_name: str):
        store = Store.find_one({'name': store_name})
        if not store:
            return await ctx.send(
                f'```fix\nCould not find store "{store_name}"\n```')
        item = Item.find_one({'name': item_name})
        if not item:
            return await ctx.send(
                f'```fix\nCould not find item "{item_name}"\n```')

        store = Prodict.from_dict(store)
        item = Prodict.from_dict(item)
        if store.item_list:
            if item.id in store['item_list']:
                return await ctx.send(
                    f'```fix\nItem "{item_name}" already exists in "{store_name}"\n```'
                )
            store.item_list.append(item.id)
            print('exist')
        else:
            print('not exist')
            store.item_list = [item.id]

        Store.save(store)

        await ctx.send(f'```css\nStocked "{item_name}" in "{store_name}"\n```')
예제 #2
0
    async def create_image(self, name, path):
        img_id = None
        path = Path(path).resolve()

        with subprocess.Popen(tar_image_cmd(path),
                              stdout=subprocess.PIPE) as proc:
            img_params = Prodict.from_dict({
                'fileobj': proc.stdout,
                'encoding': 'identity',
                'tag': name,
                'labels': def_labels(),
                'stream': True
            })

            logger.info(f"building image {img_params} from {path}")
            async for chunk in await self.dc.images.build(**img_params):
                if isinstance(chunk, dict):
                    logger.debug(chunk)
                    if 'aux' in chunk:
                        img_id = underdict(chunk['aux'])
                else:
                    logger.debug('chunk: %s %s', type(chunk), chunk)
            logger.info('image created %s', img_id)

        img = await self.dc.images.get(name)
        return Prodict.from_dict(underdict(img))
예제 #3
0
    async def view(self, ctx, store=None):
        """ View store """
        user = await author.get(ctx.author)
        store_list = ''
        stores = StoreModel.find()
        loaded_store = None
        if store is None:
            if not stores:
                return await ctx.send(f'```fix\nThere are no stores setup"\n```')
            if stores.count() == 1:
                loaded_store = Prodict.from_dict(stores[0])
                item_list = ''
                loaded_store.inventory = sorted(loaded_store.inventory, key=curried.get_in(['price']), reverse=False)
                for i, item in enumerate(loaded_store.inventory):
                    item = Prodict.from_dict(item)
                    if user['quote_to'] != 'USD':
                        rates = await coins.rate_convert()
                        item.price = rates[user['quote_to']] * item.price
                        item.payout = rates[user['quote_to']] * item.payout
                    formatted_price = currency.symbol(user["quote_to"]) + '{0:.2f}'.format(item.price)
                    formatted_payout = "{0:.2f}".format(item.payout)
                    item_list += f'{i + 1}. {item.name}{" " * (15 - len(item.name))}{formatted_price}{" " * (10 - len(formatted_price))}{item.about.format(payout=formatted_payout, rate=str(item.rate) + " minutes")}\n'
                return await ctx.send(
                    f'```py\n{user.quote_to}\nStore name: {loaded_store.name}\n\nItem{" " * (18 - len("item"))}Price{" " * (10 - len("price"))}Description\n\n{item_list}\n\nQoins represent a USD value by default, the prices will convert depending upon what quote currency you have set on your account.  Use the "{self.config.prefix[0]}sq <currency symbol>" command to change it```')
            for i, _store in enumerate(stores):
                _store = Prodict.from_dict(_store)
                item_count = len(_store.inventory)
                store_list += f'\n{i + 1}. {_store.name}{" " * (12 - len("Name"))}{item_count}{" " * (10 - len(str(item_count)))}{_store.about}'
            store_list_head = f'\nName{" " * (15 - len("Name"))}Item Count{" " * (10 - len("item"))}Description'

            await ctx.send(f'```diff\nStore list:\n{store_list_head}{store_list}\n```')
            return await ctx.send_help('view')

        store_list = []
        for i, _store in enumerate(stores):
            store_list.append(_store)
        if store.isnumeric():
            if int(store) - 1 in range(len(store_list)):
                loaded_store = store_list[int(store) - 1]
        else:
            loaded_store = StoreModel.find_one({'name': store})

        if not loaded_store:
            return await ctx.send(f'```fix\nCould not find store "{store}"\n```')

        loaded_store = Prodict.from_dict(loaded_store)
        item_list = ''
        for i, item in enumerate(loaded_store.inventory):
            item = Prodict.from_dict(item)
            if user['quote_to'] != 'USD':
                rates = await coins.rate_convert()
                item.price = rates[user['quote_to']] * item.price
                item.payout = rates[user['quote_to']] * item.payout
            formatted_price = currency.symbol(user["quote_to"]) + '{0:.2f}'.format(item.price)
            formatted_payout = "{0:.2f}".format(item.payout)

            item_list += f'{i + 1}. {item.name}{" " * (18 - len(item.name))}{formatted_price}{" " * (10 - len(formatted_price))}{item.about.format(payout=formatted_payout)}\n'
        await ctx.send(
            f'```py\n{user.quote_to}\n{loaded_store.name}\n\nItem{" " * (21 - len("item"))}Price{" " * (10 - len("price"))}Description\n\n{item_list}```')
예제 #4
0
 def __init__(self, images_path, images, container_params, container_env,
              **kwargs):
     self.dc = aiodocker.Docker()
     self.initial_ports = list(range(8900, 8999))
     self.available_ports = list(self.initial_ports)
     self.images_path = images_path
     self.images = Prodict.from_dict(images)
     self.container_env = Prodict.from_dict(container_env)
     self.container_params = Prodict.from_dict(container_params)
예제 #5
0
def completely_config(dir='.', conf='config.yaml',
                      env_fn='.env', env_local_fn='.env.local'):

    root = Path(os.getcwd())
    load_dotenv(dotenv_path=root/env_fn)
    load_dotenv(dotenv_path=root/env_local_fn)

    env = os.environ['ENV'] = os.environ.get('ENV', ENV_DEV)
    name = os.environ['NAME'] = os.getenv('NAME', socket.gethostname())

    print(__package__)

    tmplenv = Environment(
        loader=FileSystemLoader(str(root))
    )
    tmpl = tmplenv.get_template(conf)
    data = tmpl.render(**os.environ)
    data = yaml.load(data)
    data.update({
        # use pre configured or detected service name
        'name': data.get('name', name),
        'env': env
    })
    data.update(data.pop(data['name'], {}))

    return Prodict.from_dict(data)
예제 #6
0
 def run_struct(self, name, network, memory, bind_ip, host_ports,
                auto_remove, etc_hosts, env, **kwargs):
     return Prodict.from_dict({
         'Image': self.image.id,
         'Hostname': name,
         'Cmd': self.image.cmd,
         'Labels': {
             'inband': 'native'
         },
         'Env': [f"{k}={v}" for k, v in env.items()],
         'StopSignal': 'SIGTERM',
         'HostConfig': {
             # 'AutoRemove': auto_remove,
             'RestartPolicy': {
                 'Name': 'unless-stopped'
             },
             'PortBindings': {
                 p: [{
                     'HostIp': bind_ip,
                     'HostPort': str(hp)
                 }]
                 for hp, p in zip(host_ports, self.image.ports)
             },
             'ExtraHosts':
             [f"{host}:{ip}" for host, ip in etc_hosts.items()],
             'NetworkMode': network,
             'Memory': memory
         }
     })
예제 #7
0
    async def _buy(self, ctx, item_name: str):
        """ Buy an item from store """
        user = await author.get(ctx.author)
        item = Item.find_one({'name': item_name})
        if not item:
            return await ctx.send(f'```fix\nCannot find item "{item_name}"\n```')

        item = Prodict.from_dict(item)

        if any(i['id'] == item.id for i in user.item_list):
            return await ctx.send(f'```fix\nYou already own item "{item_name}"\n```')

        if user['quote_to'] != 'USD':
            rates = await coins.rate_convert()
            item.price = rates[user['quote_to']] * item.price
            item.payout = rates[user['quote_to']] * item.payout

        if user.game.in_pocket < item.price:
            return await ctx.send(f'```fix\nYou don\'t have enough money in your pocket\n```')

        user.game.in_pocket = round(user.game.in_pocket - item.price)
        user.item_list.append({
            'id': item.id,
            'last_run': datetime.now()
        })

        User.save(user)
        await ctx.send(f'```css\nYou bought an item\n```')
예제 #8
0
    async def run_container(self, name, params):

        # build custom images
        if False:

            img_path = ''
        else:
            # rebuild base image
            await self.create_image(self.images.base.name,
                                    self.images.base.path)
            # params for service image
            img_path = self.images.collection.path

        # service image
        service_img_name = f'rst/service-{name}'
        img = await self.create_image(service_img_name, img_path)

        def take_port():
            return {
                'HostIp': self.container_params.bind_ip,
                'HostPort': str(self.allocate_port())
            }

        ports = {
            port: [take_port()]
            for port in img.container_config.exposed_ports.keys() or {}
        }
        a_ports = [port[0]['HostPort'] for port in ports.values()]

        env = {'NAME': name}
        env.update(self.container_env)

        config = Prodict.from_dict({
            'Image': img.id,
            'Hostname': name,
            'Cmd': name,
            'Ports': ports,
            'Labels': def_labels(a_ports=a_ports),
            'Env': [f"{k}={v}" for k, v in env.items()],
            'StopSignal': 'SIGTERM',
            'HostConfig': {
                'RestartPolicy': {
                    'Name': 'unless-stopped'
                },
                'PortBindings': ports,
                'NetworkMode': self.container_params.network,
                'Memory': self.container_params.memory
            }
        })

        print(config)

        logger.info(f"starting container {name}. ports: {config.Ports}")
        c = await self.dc.containers.create_or_replace(name, config)
        await c.start()
        await c.show()
        c = inject_attrs(c)
        logger.info(f'started container {c.attrs.name} [{c.attrs.id}]')
        return short_info(c)
예제 #9
0
def build_options_from_req(params: Dict):
    """
    Build BuildOptions from request params
    """
    return BuildOptions(nocache=req_to_bool(params.get('nocache', None)),
                        auto_remove=req_to_bool(params.get(
                            'auto_remove', None)),
                        env=pdict.from_dict(params.get('env', {})))
예제 #10
0
    def _parse_input_config(cfg: Union[str, Path, dict]):
        if isinstance(cfg, dict):
            return Prodict.from_dict(cfg)

        if isinstance(cfg, str) and "=" in cfg:
            key, value = cfg.split("=")
            parsed_value: Any

            if value == "True":
                parsed_value = True
            elif value == "False":
                parsed_value = False
            elif re.match(r"^[-+]?[0-9]+$", value):
                parsed_value = int(value)
            elif re.match(r"^[-+]?[0-9]*\.?[0-9]+$", value):
                parsed_value = float(value)
            else:
                parsed_value = value

            key_chain = key.split(".")
            result: Dict[str, Any] = {}
            ref = result
            while len(key_chain) > 1:
                next_key = key_chain.pop(0)
                ref[next_key] = {}
                ref = ref[next_key]
            ref[key_chain.pop(0)] = parsed_value
            return Prodict.from_dict(result)

        if isinstance(cfg, str):
            cfg = Path(cfg)

        if isinstance(cfg, Path):
            if not cfg.suffix:
                cfg = cfg.with_suffix(".json")
            if not cfg.exists():
                cfg = Config.CONFIG_DIR / cfg
            if not cfg.exists():
                raise FileNotFoundError(
                    f"Given config {str(cfg)} does not exist locally or as part of wtl."
                )

            json_data = json.load(Path(cfg).open())
            return Prodict.from_dict(json_data)

        raise ValueError(f"Unexpected config input: {cfg}")
예제 #11
0
async def main(data, **params):
    data = pdict.from_dict(data)
    logger.debug('received message', **data)

    if data.message:
        await scheduler.spawn(handle_msg(data))

    return {}
예제 #12
0
 async def items(self, ctx):
     """ View items """
     items = Item.find()
     item_list = ''
     for item in items:
         item = Prodict.from_dict(item)
         item_list += f'{item.name} - {item.about}\nprice: {item.price} payout: {item.payout} rate: {item.rate}\n\n'
     await ctx.send(f'```py\nItems:\n{item_list}```')
예제 #13
0
async def portfolio_value(user, coin_list, quote_to=None):
    value = 0
    if user['game']['portfolio']['coins']:
        for coin in user['game']['portfolio']['coins']:
            if quote_to is None:
                quote_to = user["quote_to"]
            coin = Prodict.from_dict(coin)
            value += await get_value(coin.symbol, quote_to, coin.name, coin.amount, coin_list)
    return value
예제 #14
0
async def enrich(ip, **params):
    if state.geodata:
        try:
            location = state.geodata.get_location(ip, detailed=True)
            if location and 'info' in location:
                return handle_location(**pdict.from_dict(location['info']))
            return response.error('Database not ready')
        except Exception:
            logger.exception('mmgeo error')
            return response.error('Error while quering database')
예제 #15
0
 async def inventory(self, ctx):
     """ Check your item inventory """
     user = await author.get(ctx.author)
     mention = ctx.author.mention
     item_list = ''
     if user.inventory:
         for item in user.inventory:
             item = Prodict.from_dict(item)
             item_list += f'\'{item.name}\'\n'
     await ctx.send(f'```py\nInventory:\n{item_list}```{mention}')
예제 #16
0
def test_recursive_annotations3():
    r = Recursive()

    r.dynamic_dict_attr = {'a': 1, 'b': 2, 'c': 3}
    r.dynamic_prodict_attr = Prodict.from_dict({'a': 1, 'b': 2, 'c': 3})

    assert r.dynamic_dict_attr == {'a': 1, 'b': 2, 'c': 3}
    assert type(r.dynamic_dict_attr) == Prodict

    assert r.dynamic_prodict_attr == {'a': 1, 'b': 2, 'c': 3}
    assert type(r.dynamic_prodict_attr) == Prodict
예제 #17
0
def inject_attrs(cont):
    attrs = underdict(cont._container)
    attrs['name'] = (attrs['name']
                     if 'name' in attrs else attrs['names'][0]).strip('/')
    attrs['short_id'] = attrs['id'][:12]
    if 'state' in attrs and 'status' in attrs['state']:
        attrs['status'] = attrs['state']['status']
    if 'config' in attrs and 'labels' in attrs['config']:
        attrs['labels'] = attrs['config']['labels']
    cont.attrs = Prodict.from_dict(attrs)
    return cont
예제 #18
0
파일: aws.py 프로젝트: go-sari/sari-web
def rds_get_db_connection_parameters(boto3_session: boto3.session.Session, db_identifier, username) \
        -> Tuple[Prodict, str]:
    rds = boto3_session.client("rds")
    # Retrieve the basic parameters
    db_instances = rds.describe_db_instances(
        DBInstanceIdentifier=db_identifier)['DBInstances']
    db = Prodict.from_dict(db_instances[0])
    # Generate the ephemeral password
    password = rds.generate_db_auth_token(DBHostname=db.Endpoint.Address,
                                          Port=db.Endpoint.Port,
                                          DBUsername=username)
    return db, password
예제 #19
0
 def __init__(self,
              images,
              container_params,
              image_params,
              image_navigator,
              start_port=8900,
              end_port=8999,
              **kwargs):
     # instance of low-level async docker client
     self.dc = aiodocker.Docker()
     # containers images navigator
     self.image_navigator = image_navigator
     # pool start port
     self.start_port = start_port
     # pool end port
     self.end_port = end_port
     # ports reservation
     self.reserved_ports = set()
     # common container params
     self.container_params = pdict.from_dict(container_params)
     self.image_params = pdict.from_dict(image_params)
예제 #20
0
 def struct(self):
     return pdict.from_dict({
         'tag': self.img.name,
         'fileobj': self.p.stdout,
         'encoding': 'identity',
         'path_dockerfile': self.dockerfile,
         'nocache': self.img_options.get('nocache', False),
         'forcerm': self.img_options.get('forcerm', True),
         'rm': self.img_options.get('rm', True),
         'pull': self.img_options.get('pull', False),
         'stream': True
     })
예제 #21
0
def short_info(container):
    if hasattr(container, 'attrs'):
        inject_attrs(container)
    ca = container.attrs
    dic = Prodict.from_dict({
        key: getattr(container.attrs, key)
        for key in ['short_id', 'name', 'status']
    })
    dic.ports = []
    if 'labels' in ca:
        if 'ports' in ca.labels:
            dic.ports = unpack_ports(ca.labels.ports)
    return dic
예제 #22
0
def initial_model() -> Prodict:
    return Prodict.from_dict({
        "aws": {
            "regions": [AWS_REGION_US, AWS_REGION_UK],
            "single_region": False,
        },
        "okta": {
            "organization": "acme",
        },
        "applications": {},
        "job": {
            "next_transition": None,
        },
    })
예제 #23
0
async def get(author, create=True):
    user = User.find_one({"user_id": str(author.id)})
    if not user:
        if create is False:
            return False
        user_template = {
            'user_id': author.id,
            'name': author.name,
            'discriminator': author.discriminator
        }
        user_id = User.insert_one(user_template).inserted_id
        user = User.find_one({"_id": user_id})

    return Prodict.from_dict(user)
예제 #24
0
    async def run_container(self,
                            name,
                            env={},
                            nocache=None,
                            auto_remove=None,
                            **kwargs):

        image_options = dict(nocache=def_val(nocache, False),
                             **self.image_params)
        container_options = dict(auto_remove=def_val(auto_remove, False))

        logger.info('called run container (kwargs will not used)',
                    env=env,
                    func_args=dict(auto_remove=auto_remove,
                                   nocache=nocache,
                                   kwargs=kwargs),
                    image_options=image_options,
                    container_options=container_options)

        # building image
        service_img = self.image_navigator[name]
        await self.create_image(service_img, image_options)
        await self.remove_container(name)
        await asyncio.sleep(0.1)
        # preparing to run
        available_ports = await self.available_ports()
        allocated_ports = list(available_ports.pop()
                               for p in service_img.ports)
        self.hold_ports(allocated_ports)
        try:
            params = pdict.from_dict({
                **dict(host_ports=allocated_ports),
                **self.container_params
            })
            params.env.update(env)
            builder = BandContainerBuilder(service_img)
            config = builder.run_struct(name, **container_options, **params)
            # running service
            logger.info(f"starting container {name}.")
            dc = await self.dc.containers.run(config=config, name=name)
            c = BandContainer(dc)
            await c.ensure_filled()
            logger.info(f'started container {c.name} [{c.short_id}] {c.ports}')
            return c.short_info
        except Exception as exc:
            raise exc
        finally:
            self.free_ports(allocated_ports)
예제 #25
0
async def broadcast(key, name='', uid='', data={}, **params):
    """
    Track events listener to handle activity and sessions
    """
    if uid:
        project_id = data.get('projectId', None)
        now = ms()
        if project_id == settings.project_id:
            if key.startswith('in.gen.track'):
                if name == 'session':
                    sess = pdict.from_dict(data.get('sess', None))
                    phone = rand_item(state.free_phones())
                    if sess and sess.num and phone:
                        # if sess['type'] == 'campaign': and others...
                        # look at session description https://rock.st/docs/reference/web-sdk/sessions/
                        await state.set_user(uid, dict(sess_no=sess['num'], phone=phone, start=now, act=now))
        await state.touch(uid)
예제 #26
0
    def test_aws_gather_rds_info(self, region: str,
                                 present_instances: List[str],
                                 absent_instances: List[str]):
        # Given:
        _create_subnets("db_subnet", region, "10.0.0.0/16",
                        [("a", "10.0.1.0/24"), ("b", "10.0.2.0/24")])
        rds = boto3.client("rds", region_name=region)
        instances = present_instances
        instances.insert(0, "acme-test")
        for index, db_id in enumerate(instances):
            rds.create_db_instance(
                DBInstanceIdentifier=db_id,
                Engine="mysql",
                EngineVersion="5.7.28",
                DBName=f"db_{db_id}",
                MasterUsername="******",
                DBInstanceClass="db.m1.small",
                MultiAZ=True,
                AvailabilityZone=f"{AWS_REGION_UK}a",
                VpcSecurityGroupIds=[random_security_group_id()],
                DBSubnetGroupName="db_subnet",
            )
        aws = AwsClient(region)
        gatherer = DatabaseInfoGatherer(
            aws, MasterPasswordResolver(aws, MASTER_PASSWORD_DEFAULTS))
        model = initial_model()
        model.aws["databases"] = Prodict.from_dict(RDS_CONFIG_DATABASES)
        local_databases = {
            k: v
            for k, v in RDS_INFO_DATABASES.items()
            if k.startswith(f"{region}/")
        }

        # When:
        resp, issues = gatherer.gather(model)

        # Then:
        assert len(issues) == 1 + len(absent_instances)
        assert issues[0].level == IssueLevel.WARNING
        assert issues[0].type == "DB"
        assert issues[0].id == f"{region}/acme-test"
        for index, db_id in enumerate(absent_instances, 1):
            assert issues[index].level == IssueLevel.ERROR
            assert issues[index].type == "DB"
            assert issues[index].id == f"{region}/{db_id}"
        assert_dict_equals(resp, {"aws": {"databases": local_databases}})
예제 #27
0
def run(config):
    with open('./config/' + config) as config_file:
        plot_config = Prodict.from_dict(
            yaml.load(config_file, Loader=yaml.FullLoader))
    plot_data = {'x': None, 'y': []}
    with open('./data/' + plot_config.data_file, newline='') as data_file:
        csv_reader = csv.reader(data_file, delimiter=',')
        counter = 0
        for row in csv_reader:
            if counter == 0:
                plot_data['x'] = row
            else:
                plot_data['y'].append(row)
            counter += 1

    if (plot_config.plot_type == 'bar'):
        barPlot(plot_config, plot_data)
    elif (plot_config.plot_type == 'line'):
        linePlot(plot_config, plot_data, 2)
예제 #28
0
    def test_cfg_gather_applications_config(self):
        # Given:
        model = initial_model()
        model.aws["databases"] = dict_deep_merge(
            Prodict.from_dict(RDS_CONFIG_DATABASES), RDS_INFO_DATABASES)
        app_config = ApplicationConfigGatherer("tests/data/applications.yaml")

        # When:
        resp, issues = app_config.gather(model)

        # Then:
        assert len(issues) == 0
        assert_dict_equals(
            resp, {
                "applications": {
                    "monitoring":
                    ["us-east-1/borders", "eu-west-2/blackwells"]
                }
            })
예제 #29
0
 async def collect(self, ctx):
     """ Collect Qoins generated from items """
     user = await author.get(ctx.author)
     mention = ctx.author.mention
     now = datetime.now()
     total_earned = 0
     item_earned = ''
     for i, item in enumerate(user.item_list):
         _item = Prodict.from_dict(Item.find_one({"_id": item['id']}))
         if user.quote_to != 'USD':
             rates = await coins.rate_convert('USD')
             _item.payout = rates[user.quote_to] * _item.payout
         if item['last_run'] is None:
             earned = int(_item.payout)
             item_earned += f'\n+{earned} {self.config.economy.currency_name} - {_item.name}'
             total_earned += earned
             user.item_list[i]['last_run'] = datetime.now()
         else:
             difference = relativedelta.relativedelta(now, item['last_run'])
             item_multiplier = float(
                 '{0:.2f}'.format((difference.hours * 60) +
                                  difference.minutes +
                                  (difference.seconds / 60)))
             print(item_multiplier)
             earned = 0
             if item_multiplier < _item.rate:
                 wait_time = int(_item.rate - difference.minutes) - 1
                 item_earned += f'\n{_item.name} - {wait_time} minutes left'
                 continue
             else:
                 earned = float('{0:.2f}'.format(
                     (_item.payout * (item_multiplier / _item.rate))))
             item_earned += f'\n+{earned} {self.config.economy.currency_name} - {_item.name}'
             total_earned += earned
             user.item_list[i]['last_run'] = datetime.now()
     if float(total_earned) > 0:
         user.game.in_pocket = user.game.in_pocket + total_earned
         User.save(user)
     empty_message = f'You don\'t have any items'
     total_earned = '{0:.2f}'.format(total_earned)
     await ctx.send(
         f'```diff\n{"+" + total_earned if float(total_earned) > 0 else 0} {self.config.economy.currency_name} collected from items\n``````diff\n{item_earned if item_earned else empty_message}\n```{mention}'
     )
예제 #30
0
async def checker():
    scheduler = await aiojobs.create_scheduler(limit=CONCURRENT_CHECKS)
    while True:
        state.myloop()
        try:
            params = settings.proxy_checker
            headers = {"Authorization": params.auth}
            async with aiohttp.ClientSession(headers=headers) as s:
                async with s.get(params.list, timeout=5) as r:
                    for proxy in await r.json():
                        p = Prodict.from_dict(proxy)
                        await scheduler.spawn(chech(p, params))                     
        except Exception:
            logger.exception('err')
        jobs = scheduler._jobs
        while True:
            await asyncio.sleep(0.1)
            if len(jobs) == 0:
                logger.info('finished')
                break