Beispiel #1
0
 def test_get_docker_run_command(self):
     targets = [('key', BundleTarget('uuid1', '')),
                ('key2', BundleTarget('uuid2', ''))]
     bundle_locations = {'uuid1': 'local/path1', 'uuid2': 'local/path2'}
     session = InteractiveSession('some-docker-image',
                                  dependencies=targets,
                                  bundle_locations=bundle_locations)
     expected_regex = (
         'docker run -it --name interactive-session-0x[a-z0-9]{32} -w \/0x[a-z0-9]{32} -v '
         '[\s\S]{0,100}local\/path1:\/0x[a-z0-9]{32}\/key:ro -v [\s\S]{0,100}local\/path2:\/0x[a-z0-9]{32}\/key2:ro '
         'some-docker-image bash')
     self.assertTrue(
         re.match(expected_regex, session.get_docker_run_command()))
 def test_get_docker_run_command(self):
     targets = [('key', BundleTarget('uuid1', '')), ('key2', BundleTarget('uuid2', ''))]
     bundle_locations = {'uuid1': 'local/path1', 'uuid2': 'local/path2'}
     session = InteractiveSession(
         'some-docker-image', dependencies=targets, bundle_locations=bundle_locations
     )
     session._host_bash_history_path = ".bash_history"
     expected_regex = (
         'docker run -it --name interactive-session-0x[a-z0-9]{32} -w \/0x[a-z0-9]{32} '
         '-e HOME=\/0x[a-z0-9]{32} -e HISTFILE=\/usr\/sbin\/\.bash_history -e PROMPT_COMMAND="history -a" -u \$\(id -u\):\$\(id -g\) '
         '-v [\s\S]{0,100}local\/path1:\/0x[a-z0-9]{32}\/key:ro -v [\s\S]{0,100}local\/path2:\/0x[a-z0-9]{32}\/key2:ro '
         '-v \.bash_history:\/usr\/sbin\/\.bash_history:rw -v [\s\S]{0,100}\/0x[a-z0-9]{32}:\/0x[a-z0-9]{32}:rw some-docker-image'
     )
     self.assertTrue(re.match(expected_regex, session.get_docker_run_command()))
 def test_missing_bundle_location(self):
     try:
         targets = [
             ('key', BundleTarget('uuid1', 'sub/path1')),
             ('key2', BundleTarget('uuid2', 'sub/path2')),
         ]
         # Missing a location of uuid2 bundle
         bundle_locations = {'uuid1': 'local/path1'}
         session = InteractiveSession(
             'some-docker-image', dependencies=targets, bundle_locations=bundle_locations
         )
         session.start()
     except Exception as e:
         self.assertEqual(str(e), 'Missing bundle location for bundle uuid: uuid2')
         return
     self.fail('Should have thrown an error for the missing bundle location')
        def _fetch_chunk(self, path, chunk_id):
            '''
            Fetch and return a chunk from the cache, or with the client as necessary.
            Refreshes chunks that are older than self.timeout
            Only save full chunks to the cache.
            Partial chunks (i.e. at the end of a file) are not cached because they could grow.
            '''

            now = int(time.time())
            key = (path, chunk_id)

            if key in self.cache:
                t, arr = self.cache[key]
                if now - t < self.timeout:
                    return arr  # return chunk from cache
                else:
                    self.cache.pop(key)  # pop out expired entry

            # grab from client
            byte_range = (
                chunk_id * self.chunk_size,
                chunk_id * self.chunk_size + self.chunk_size - 1,
            )
            with closing(
                self.client.fetch_contents_blob(BundleTarget(self.bundle_uuid, path), byte_range)
            ) as contents:
                arr = contents.read()

            if len(arr) == self.chunk_size:  # only cache if fetched full chunk
                if len(self.cache) >= self.max_num_chunks:  # if full, remove the oldest item
                    self.cache.popitem(last=False)
                self.cache[key] = (now, arr)

            return arr
Beispiel #5
0
    def get_target_info(self, run_state, path, args, reply_fn):
        """
        Return target_info of path in bundle as a message on the reply_fn
        """
        target_info = None
        dep_paths = set([dep.child_path for dep in run_state.bundle.dependencies])

        # if path is a dependency raise an error
        if path and os.path.normpath(path) in dep_paths:
            err = (
                http.client.NOT_FOUND,
                '{} not found in bundle {}'.format(path, run_state.bundle.uuid),
            )
            reply_fn(err, None, None)
            return
        else:
            try:
                target_info = download_util.get_target_info(
                    run_state.bundle_path, BundleTarget(run_state.bundle.uuid, path), args['depth']
                )
            except PathException as e:
                err = (http.client.NOT_FOUND, str(e))
                reply_fn(err, None, None)
                return

        if not path and args['depth'] > 0:
            target_info['contents'] = [
                child for child in target_info['contents'] if child['name'] not in dep_paths
            ]
        # Object is not JSON serializable so submit its dict in API response
        # The client is responsible for deserializing it
        target_info['resolved_target'] = target_info['resolved_target'].__dict__
        reply_fn(None, {'target_info': target_info}, None)
 def _get_info(self, path):
     ''' Set a request through the json api client to get info about the bundle '''
     try:
         info = self.client.fetch_contents_info(BundleTarget(self.bundle_uuid, path), 1)
     except NotFoundError:
         raise FuseOSError(errno.ENOENT)
     return info
 def test_get_docker_run_command_with_subpaths(self):
     targets = [
         ('key', BundleTarget('uuid1', 'sub/path1')),
         ('key2', BundleTarget('uuid2', 'sub/path2')),
     ]
     bundle_locations = {'uuid1': 'local/path1', 'uuid2': 'local/path2'}
     session = InteractiveSession('some-docker-image',
                                  dependencies=targets,
                                  bundle_locations=bundle_locations)
     session._host_bash_history_path = ".bash_history"
     expected_regex = (
         'docker run -it --name interactive-session-0x[a-z0-9]{32} -w \/0x[a-z0-9]{32} -u 1 -v '
         '[\s\S]{0,100}local\/path1/sub/path1:\/0x[a-z0-9]{32}\/key:ro -v [\s\S]{0,100}local\/path2/sub/path2'
         ':\/0x[a-z0-9]{32}\/key2:ro -v \.bash_history:\/usr\/sbin\/\.bash_history:rw some-docker-image bash'
     )
     self.assertTrue(
         re.match(expected_regex, session.get_docker_run_command()))
Beispiel #8
0
 def test_single_file(self):
     """Test getting target info of a single file (compressed as .gz) on Azure Blob Storage."""
     bundle_uuid, bundle_path = self.create_file(b"a")
     target_info = get_target_info(bundle_path, BundleTarget(bundle_uuid, None), 0)
     target_info.pop("resolved_target")
     self.assertEqual(
         target_info, {'name': bundle_uuid, 'type': 'file', 'size': 1, 'perm': 0o755}
     )
Beispiel #9
0
    def test_bundle_single_file(self):
        """Running get_target_info for a bundle with a single file."""
        bundle = self.create_run_bundle()
        self.save_bundle(bundle)
        self.upload_file(bundle, b"hello world")
        target = BundleTarget(bundle.uuid, "")

        info = self.download_manager.get_target_info(target, 0)
        self.assertEqual(info["name"], bundle.uuid)
        self.assertEqual(info["size"], 11)
        self.assertEqual(info["perm"], self.DEFAULT_PERM)
        self.assertEqual(info["type"], "file")
        self.assertEqual(str(info["resolved_target"]), f"{bundle.uuid}:")
        self.check_file_target_contents(target)
Beispiel #10
0
 def fetch_contents_info(self, target, depth=0):
     """
     Calls download_manager.get_target_info server-side and returns the target_info.
     For details on return value look at worker.download_util.get_target_info
     :param target: a worker.download_util.BundleTarget
     """
     request_path = '/bundles/%s/contents/info/%s' % (
         target.bundle_uuid,
         urllib.parse.quote(target.subpath),
     )
     response = self._make_request('GET', request_path, query_params={'depth': depth})
     # Deserialize the target. See /rest/bundles/_fetch_contents_info for serialization side
     response['data']['resolved_target'] = BundleTarget.from_dict(
         response['data']['resolved_target']
     )
     return response['data']
Beispiel #11
0
 def _threaded_read(self, run_state, path, stream_fn, reply_fn):
     """
     Given a run state, a path, a stream function and a reply function,
         - Computes the real filesystem path to the path in the bundle
         - In case of error, invokes reply_fn with an http error
         - Otherwise starts a thread calling stream_fn on the computed final path
     """
     try:
         final_path = get_target_path(
             run_state.bundle_path, BundleTarget(run_state.bundle.uuid,
                                                 path))
     except PathException as e:
         reply_fn((http.client.NOT_FOUND, str(e)), None, None)
     read_thread = threading.Thread(target=stream_fn, args=[final_path])
     read_thread.start()
     self.read_threads.append(read_thread)
    def __call__(self, prefix, action=None, parser=None, parsed_args=None):
        key, target = cli_util.parse_key_target(prefix)
        if target is None:
            return ()

        instance, worksheet_spec, bundle_spec, subpath = cli_util.parse_target_spec(
            target)

        if worksheet_spec is None:
            worksheet_spec = getattr(parsed_args, 'worksheet_spec', None)

        client, worksheet_uuid = self.cli.parse_client_worksheet_uuid(
            worksheet_spec)

        # Build parameterizable format string for suggestions
        suggestion_format = ''.join([
            (key + ':') if key is not None else '',
            (worksheet_spec + '//') if worksheet_spec is not None else '',
            (bundle_spec + '/') if subpath is not None else '',
            '{}',
        ])

        if subpath is None:
            # then suggest completions for bundle_spec
            return (suggestion_format.format(b) for b in BundlesCompleter(
                self.cli)(bundle_spec, action, parsed_args, worksheet_uuid))
        else:
            # then suggest completions for subpath
            client, worksheet_uuid, resolved_target = self.cli.resolve_target(
                client, worksheet_uuid, target)
            dir_target = BundleTarget(resolved_target.bundle_uuid,
                                      os.path.dirname(subpath))
            try:
                info = client.fetch_contents_info(dir_target, depth=1)
            except NotFoundError:
                return ()
            if info['type'] == 'directory':
                matching_child_names = []
                basename = os.path.basename(subpath)
                for child in info['contents']:
                    if child['name'].startswith(basename):
                        matching_child_names.append(child['name'])
                return (suggestion_format.format(
                    os.path.join(dir_target.subpath, child_name))
                        for child_name in matching_child_names)
            else:
                return ()
def _fetch_bundle_contents_info(uuid, path=''):
    """
    Fetch metadata of the bundle contents or a subpath within the bundle.

    Query parameters:
    - `depth`: recursively fetch subdirectory info up to this depth.
      Default is 0.

    Response format:
    ```
    {
      "data": {
          "name": "<name of file or directory>",
          "link": "<string representing target if file is a symbolic link>",
          "type": "<file|directory|link>",
          "size": <size of file in bytes>,
          "perm": <unix permission integer>,
          "contents": [
              {
                "name": ...,
                <each file of directory represented recursively with the same schema>
              },
              ...
          ]
      }
    }
    ```
    """
    depth = query_get_type(int, 'depth', default=0)
    target = BundleTarget(uuid, path)
    if depth < 0:
        abort(http.client.BAD_REQUEST, "Depth must be at least 0")

    check_bundles_have_read_permission(local.model, request.user, [uuid])
    try:
        info = local.download_manager.get_target_info(target, depth)
        # Object is not JSON serializable so submit its dict in API response
        # The client is responsible for deserializing it
        info['resolved_target'] = info['resolved_target'].__dict__
    except NotFoundError as e:
        abort(http.client.NOT_FOUND, str(e))
    except Exception as e:
        abort(http.client.BAD_REQUEST, str(e))

    return {'data': info}
def _fetch_bundle_contents_blob(uuid, path=''):
    """
    API to download the contents of a bundle or a subpath within a bundle.

    For directories, this method always returns a tarred and gzipped archive of
    the directory.

    For files, if the request has an Accept-Encoding header containing gzip,
    then the returned file is gzipped. Otherwise, the file is returned as-is.

    HTTP Request headers:
    - `Range: bytes=<start>-<end>`: fetch bytes from the range
      `[<start>, <end>)`.
    - `Accept-Encoding: <encoding>`: indicate that the client can accept
      encoding `<encoding>`. Currently only `gzip` encoding is supported.

    Query parameters:
    - `head`: number of lines to fetch from the beginning of the file.
      Default is 0, meaning to fetch the entire file.
    - `tail`: number of lines to fetch from the end of the file.
      Default is 0, meaning to fetch the entire file.
    - `max_line_length`: maximum number of characters to fetch from each line,
      if either `head` or `tail` is specified. Default is 128.

    HTTP Response headers (for single-file targets):
    - `Content-Disposition: inline; filename=<bundle name or target filename>`
    - `Content-Type: <guess of mimetype based on file extension>`
    - `Content-Encoding: [gzip|identity]`
    - `Target-Type: file`

    HTTP Response headers (for directories):
    - `Content-Disposition: attachment; filename=<bundle or directory name>.tar.gz`
    - `Content-Type: application/gzip`
    - `Content-Encoding: identity`
    - `Target-Type: directory`
    """
    byte_range = get_request_range()
    head_lines = query_get_type(int, 'head', default=0)
    tail_lines = query_get_type(int, 'tail', default=0)
    truncation_text = query_get_type(str, 'truncation_text', default='')
    max_line_length = query_get_type(int, 'max_line_length', default=128)
    check_bundles_have_read_permission(local.model, request.user, [uuid])
    target = BundleTarget(uuid, path)

    try:
        target_info = local.download_manager.get_target_info(target, 0)
        if target_info['resolved_target'] != target:
            check_bundles_have_read_permission(
                local.model, request.user,
                [target_info['resolved_target'].bundle_uuid])
        target = target_info['resolved_target']
    except NotFoundError as e:
        abort(http.client.NOT_FOUND, str(e))
    except Exception as e:
        abort(http.client.BAD_REQUEST, str(e))

    # Figure out the file name.
    bundle_name = local.model.get_bundle(target.bundle_uuid).metadata.name
    if not path and bundle_name:
        filename = bundle_name
    else:
        filename = target_info['name']

    if target_info['type'] == 'directory':
        if byte_range:
            abort(http.client.BAD_REQUEST,
                  'Range not supported for directory blobs.')
        if head_lines or tail_lines:
            abort(http.client.BAD_REQUEST,
                  'Head and tail not supported for directory blobs.')
        # Always tar and gzip directories
        gzipped_stream = False  # but don't set the encoding to 'gzip'
        mimetype = 'application/gzip'
        filename += '.tar.gz'
        fileobj = local.download_manager.stream_tarred_gzipped_directory(
            target)
    elif target_info['type'] == 'file':
        # Let's gzip to save bandwidth.
        # For simplicity, we do this even if the file is already a packed
        # archive (which should be relatively rare).
        # The browser will transparently decode the file.
        gzipped_stream = request_accepts_gzip_encoding()

        # Since guess_type() will interpret '.tar.gz' as an 'application/x-tar' file
        # with 'gzip' encoding, which would usually go into the Content-Encoding
        # header. But if the bundle contents is actually a packed archive, we don't
        # want the client to automatically decompress the file, so we don't want to
        # set the Content-Encoding header. Instead, if guess_type() detects an
        # archive, we just set mimetype to indicate an arbitrary binary file.
        mimetype, encoding = mimetypes.guess_type(filename, strict=False)
        if encoding is not None:
            mimetype = 'application/octet-stream'

        if byte_range and (head_lines or tail_lines):
            abort(http.client.BAD_REQUEST,
                  'Head and range not supported on the same request.')
        elif byte_range:
            start, end = byte_range
            fileobj = local.download_manager.read_file_section(
                target, start, end - start + 1, gzipped_stream)
        elif head_lines or tail_lines:
            fileobj = local.download_manager.summarize_file(
                target, head_lines, tail_lines, max_line_length,
                truncation_text, gzipped_stream)
        else:
            fileobj = local.download_manager.stream_file(
                target, gzipped_stream)
    else:
        # Symlinks.
        abort(http.client.FORBIDDEN,
              'Cannot download files of this type (%s).' % target_info['type'])

    # Set headers.
    response.set_header('Content-Type', mimetype or 'text/plain')
    response.set_header('Content-Encoding',
                        'gzip' if gzipped_stream else 'identity')
    if target_info['type'] == 'file':
        response.set_header('Content-Disposition',
                            'inline; filename="%s"' % filename)
    else:
        response.set_header('Content-Disposition',
                            'attachment; filename="%s"' % filename)
    response.set_header('Target-Type', target_info['type'])

    return fileobj
Beispiel #15
0
def interpret_file_genpath(target_cache, bundle_uuid, genpath, post):
    """
    |cache| is a mapping from target (bundle_uuid, subpath) to the info map,
    which is to be read/written to avoid reading/parsing the same file many
    times.
    |genpath| specifies the subpath and various fields (e.g., for
    /stats:train/errorRate, subpath = 'stats', key = 'train/errorRate').
    |post| function to apply to the resulting value.
    Return the string value.
    """
    MAX_LINES = 10000  # Maximum number of lines we need to read from a file.

    # Load the file
    if not is_file_genpath(genpath):
        raise UsageError('Not file genpath: %s' % genpath)
    genpath = genpath[1:]
    if ':' in genpath:  # Looking for a particular key in the file
        subpath, key = genpath.split(':')
    else:
        subpath, key = genpath, None

    target = BundleTarget(bundle_uuid, subpath)
    if target not in target_cache:
        info = None
        try:
            target_info = rest_util.get_target_info(target, 0)
            if target_info['type'] == 'file':
                contents = head_target(target_info['resolved_target'],
                                       MAX_LINES)

                if len(contents) == 0:
                    info = ''
                elif all('\t' in x for x in contents):
                    # Tab-separated file (key\tvalue\nkey\tvalue...)
                    info = {}
                    for x in contents:
                        kv = x.strip().split("\t", 1)
                        if len(kv) == 2:
                            info[kv[0]] = kv[1]
                else:
                    try:
                        # JSON file
                        info = json.loads(''.join(contents))
                    except (TypeError, ValueError):
                        try:
                            # YAML file
                            # Use safe_load because yaml.load() could execute
                            # arbitrary Python code
                            info = yaml.safe_load(''.join(contents))
                        except yaml.YAMLError:
                            # Plain text file
                            info = ''.join(contents)
        except NotFoundError:
            pass

        # Try to interpret the structure of the file by looking inside it.
        target_cache[target] = info

    # Traverse the info object.
    info = target_cache.get(target, None)
    if key is not None and info is not None:
        for k in key.split('/'):
            if isinstance(info, dict):
                info = info.get(k, None)
            elif isinstance(info, list):
                try:
                    info = info[int(k)]
                except (KeyError, ValueError):
                    info = None
            else:
                info = None
            if info is None:
                break
    return apply_func(post, info)
Beispiel #16
0
def resolve_interpreted_blocks(interpreted_blocks, brief):
    """
    Called by the web interface.  Takes a list of interpreted worksheet
    items (returned by worksheet_util.interpret_items) and fetches the
    appropriate information, replacing the 'interpreted' field in each item.
    The result can be serialized via JSON.
    """
    def set_error_data(block_index, message):
        interpreted_blocks[block_index] = (MarkupBlockSchema().load({
            'id':
            block_index,
            'text':
            'ERROR: ' + message
        }).data)

    for block_index, block in enumerate(interpreted_blocks):
        if block is None:
            continue
        mode = block['mode']

        try:
            # Replace data with a resolved version.
            if mode in (BlockModes.markup_block, BlockModes.placeholder_block):
                # no need to do anything
                pass
            elif mode == BlockModes.record_block or mode == BlockModes.table_block:
                # header_name_posts is a list of (name, post-processing) pairs.
                # Request information
                if brief:
                    # In brief mode, only calculate whether we should interpret genpaths, and if so, set status to briefly_loaded.
                    should_interpret_genpaths = (len(
                        get_genpaths_table_contents_requests(block['rows'])) >
                                                 0)
                    block['status'] = (
                        FetchStatusSchema.get_briefly_loaded_status()
                        if should_interpret_genpaths else
                        FetchStatusSchema.get_ready_status())
                else:
                    block['rows'] = interpret_genpath_table_contents(
                        block['rows'])
                    block['status'] = FetchStatusSchema.get_ready_status()
            elif mode == BlockModes.contents_block or mode == BlockModes.image_block:
                bundle_uuid = block['bundles_spec']['bundle_infos'][0]['uuid']
                target_path = block['target_genpath']
                target = BundleTarget(bundle_uuid, target_path)
                try:
                    target_info = rest_util.get_target_info(target, 0)
                    if target_info[
                            'type'] == 'directory' and mode == BlockModes.contents_block:
                        block['status']['code'] = FetchStatusCodes.ready
                        block['lines'] = ['<directory>']
                    elif target_info['type'] == 'file':
                        block['status']['code'] = FetchStatusCodes.ready
                        if mode == BlockModes.contents_block:
                            block['lines'] = head_target(
                                target_info['resolved_target'],
                                block['max_lines'])
                        elif mode == BlockModes.image_block:
                            block['status']['code'] = FetchStatusCodes.ready
                            block['image_data'] = base64.b64encode(
                                bytes(
                                    cat_target(target_info['resolved_target']))
                            ).decode('utf-8')
                    else:
                        block['status']['code'] = FetchStatusCodes.not_found
                        if mode == BlockModes.contents_block:
                            block['lines'] = None
                        elif mode == BlockModes.image_block:
                            block['image_data'] = None
                except NotFoundError:
                    block['status']['code'] = FetchStatusCodes.not_found
                    if mode == BlockModes.contents_block:
                        block['lines'] = None
                    elif mode == BlockModes.image_block:
                        block['image_data'] = None

            elif mode == BlockModes.graph_block:
                # data = list of {'target': ...}
                # Add a 'points' field that contains the contents of the target.
                for info in block['trajectories']:
                    target = BundleTarget(info['bundle_uuid'],
                                          info['target_genpath'])
                    try:
                        target_info = rest_util.get_target_info(target, 0)
                    except NotFoundError:
                        continue
                    if target_info['type'] == 'file':
                        contents = head_target(target_info['resolved_target'],
                                               block['max_lines'])
                        # Assume TSV file without header for now, just return each line as a row
                        info['points'] = points = []
                        for line in contents:
                            row = line.split('\t')
                            points.append(row)
            elif mode == BlockModes.subworksheets_block:
                # do nothing
                pass
            elif mode == BlockModes.schema_block:
                pass
            else:
                raise UsageError('Invalid display mode: %s' % mode)

        except UsageError as e:
            set_error_data(block_index, str(e))

        except Exception:
            import traceback

            traceback.print_exc()
            set_error_data(block_index, "Unexpected error interpreting item")

        block['is_refined'] = True

    return interpreted_blocks
 def test_not_found(self):
     """Running get_target_info for a nonexistent bundle should raise an error."""
     with self.assertRaises(NotFoundError):
         target = BundleTarget(generate_uuid(), "")
         self.download_manager.get_target_info(target, 0)
    def test_bundle_folder(self):
        """Running get_target_info for a bundle with a folder, and with subpaths."""
        bundle = self.create_run_bundle()
        self.save_bundle(bundle)
        self.upload_folder(bundle, [("item.txt", b"hello world"),
                                    ("src/item2.txt", b"hello world")])
        self.assertEqual(bundle.is_dir, True)
        self.assertEqual(bundle.storage_type, self.storage_type)

        target = BundleTarget(bundle.uuid, "")
        info = self.download_manager.get_target_info(target, 2)
        self.assertEqual(info["name"], bundle.uuid)
        self.assertEqual(info["type"], "directory")
        self.assertEqual(str(info["resolved_target"]), f"{bundle.uuid}:")
        # Directory size can vary based on platform, so removing it before checking equality.
        info["contents"][0].pop("size")
        info["contents"][1].pop("size")
        self.assertEqual(
            sorted(info["contents"], key=lambda x: x["name"]),
            sorted(
                [
                    {
                        'name': 'item.txt',
                        'perm': self.DEFAULT_PERM_FILE,
                        'type': 'file'
                    },
                    {
                        'name':
                        'src',
                        'perm':
                        self.DEFAULT_PERM_DIR,
                        'type':
                        'directory',
                        'contents': [{
                            'name': 'item2.txt',
                            'size': 11,
                            'perm': self.DEFAULT_PERM_FILE,
                            'type': 'file',
                        }],
                    },
                ],
                key=lambda x: x["name"],
            ),
        )
        self.check_folder_target_contents(
            target,
            expected_members=['.', './item.txt', './src', './src/item2.txt'])

        target = BundleTarget(bundle.uuid, "item.txt")
        info = self.download_manager.get_target_info(target, 0)
        self.assertEqual(info["name"], "item.txt")
        self.assertEqual(info["type"], "file")
        self.assertEqual(str(info["resolved_target"]),
                         f"{bundle.uuid}:item.txt")
        self.check_file_target_contents(target)

        target = BundleTarget(bundle.uuid, "src")
        info = self.download_manager.get_target_info(target, 1)
        self.assertEqual(info["name"], "src")
        self.assertEqual(info["type"], "directory")
        self.assertEqual(str(info["resolved_target"]), f"{bundle.uuid}:src")
        self.assertEqual(
            info["contents"],
            [{
                'name': 'item2.txt',
                'size': 11,
                'perm': self.DEFAULT_PERM_FILE,
                'type': 'file'
            }],
        )
        self.check_folder_target_contents(
            target, expected_members=['.', './item2.txt'])

        target = BundleTarget(bundle.uuid, "src/item2.txt")
        info = self.download_manager.get_target_info(target, 0)
        self.assertEqual(info["name"], "item2.txt")
        self.assertEqual(info["type"], "file")
        self.assertEqual(str(info["resolved_target"]),
                         f"{bundle.uuid}:src/item2.txt")
        self.check_file_target_contents(target)
Beispiel #19
0
 def test_single_txt_file(self):
     """Test getting target info of a single txt file on Azure Blob Storage. As this isn't supported
     (paths should be specified within existing .gz / .tar.gz files), this should throw an exception."""
     bundle_uuid, bundle_path = self.create_txt_file(b"a")
     with self.assertRaises(PathException):
         get_target_info(bundle_path, BundleTarget(bundle_uuid, None), 0)
Beispiel #20
0
    def test_nested_directories(self):
        """Test getting target info of different files within a bundle that consists of nested directories, on Azure Blob Storage."""
        bundle_uuid, bundle_path = self.create_directory()

        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, None), 0)
        target_info.pop("resolved_target")
        self.assertEqual(target_info, {
            'name': bundle_uuid,
            'type': 'directory',
            'size': 249,
            'perm': 0o755
        })

        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, None), 1)
        target_info.pop("resolved_target")
        self.assertEqual(
            target_info,
            {
                'name':
                bundle_uuid,
                'type':
                'directory',
                'size':
                249,
                'perm':
                0o755,
                'contents': [
                    {
                        'name': 'README.md',
                        'type': 'file',
                        'size': 11,
                        'perm': 0o644
                    },
                    {
                        'name': 'dist',
                        'type': 'directory',
                        'size': 0,
                        'perm': 0o644
                    },
                    {
                        'name': 'src',
                        'type': 'directory',
                        'size': 0,
                        'perm': 0o644
                    },
                ],
            },
        )

        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, "README.md"),
                                      1)
        target_info.pop("resolved_target")
        self.assertEqual(target_info, {
            'name': 'README.md',
            'type': 'file',
            'size': 11,
            'perm': 0o644
        })

        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, "src/test.sh"),
                                      1)
        target_info.pop("resolved_target")
        self.assertEqual(target_info, {
            'name': 'test.sh',
            'type': 'file',
            'size': 7,
            'perm': 0o644
        })

        target_info = get_target_info(
            bundle_path, BundleTarget(bundle_uuid, "dist/a/b/test2.sh"), 1)
        target_info.pop("resolved_target")
        self.assertEqual(target_info, {
            'name': 'test2.sh',
            'type': 'file',
            'size': 8,
            'perm': 0o644
        })

        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, "src"), 1)
        target_info.pop("resolved_target")
        self.assertEqual(
            target_info,
            {
                'name':
                'src',
                'type':
                'directory',
                'size':
                0,
                'perm':
                0o644,
                'contents': [{
                    'name': 'test.sh',
                    'type': 'file',
                    'size': 7,
                    'perm': 0o644
                }],
            },
        )

        # Return all depths
        target_info = get_target_info(bundle_path,
                                      BundleTarget(bundle_uuid, "dist/a"), 999)
        target_info.pop("resolved_target")

        self.assertEqual(
            target_info,
            {
                'name':
                'a',
                'size':
                0,
                'perm':
                0o644,
                'type':
                'directory',
                'contents': [{
                    'name':
                    'b',
                    'size':
                    0,
                    'perm':
                    0o644,
                    'type':
                    'directory',
                    'contents': [{
                        'name': 'test2.sh',
                        'size': 8,
                        'perm': 0o644,
                        'type': 'file'
                    }],
                }],
            },
        )