Exemplo n.º 1
0
    def publish_collection(self, collection_path):
        """
        Publishes a collection to a Galaxy server and returns the import task URI.

        :param collection_path: The path to the collection tarball to publish.
        :return: The import task URI that contains the import results.
        """
        display.display("Publishing collection artifact '%s' to %s %s" %
                        (collection_path, self.name, self.api_server))

        b_collection_path = to_bytes(collection_path,
                                     errors='surrogate_or_strict')
        if not os.path.exists(b_collection_path):
            raise AnsibleError(
                "The collection path specified '%s' does not exist." %
                to_native(collection_path))
        elif not tarfile.is_tarfile(b_collection_path):
            raise AnsibleError(
                "The collection path specified '%s' is not a tarball, use 'ansible-galaxy collection "
                "build' to create a proper release artifact." %
                to_native(collection_path))

        with open(b_collection_path, 'rb') as collection_tar:
            sha256 = secure_hash_s(collection_tar.read(),
                                   hash_func=hashlib.sha256)

        content_type, b_form_data = prepare_multipart({
            'sha256': sha256,
            'file': {
                'filename': b_collection_path,
                'mime_type': 'application/octet-stream',
            },
        })

        headers = {
            'Content-type': content_type,
            'Content-length': len(b_form_data),
        }

        if 'v3' in self.available_api_versions:
            n_url = _urljoin(self.api_server,
                             self.available_api_versions['v3'], 'artifacts',
                             'collections') + '/'
        else:
            n_url = _urljoin(self.api_server,
                             self.available_api_versions['v2'],
                             'collections') + '/'

        resp = self._call_galaxy(
            n_url,
            args=b_form_data,
            headers=headers,
            method='POST',
            auth_required=True,
            error_context_msg='Error when publishing collection to %s (%s)' %
            (self.name, self.api_server))

        return resp['task']
Exemplo n.º 2
0
def main():
    argument_spec = url_argument_spec()
    argument_spec.update(
        dest=dict(type='path'),
        url_username=dict(type='str', aliases=['user']),
        url_password=dict(type='str', aliases=['password'], no_log=True),
        body=dict(type='raw'),
        body_format=dict(type='str', default='raw', choices=['form-urlencoded', 'json', 'raw', 'form-multipart']),
        src=dict(type='path'),
        method=dict(type='str', default='GET'),
        return_content=dict(type='bool', default=False),
        follow_redirects=dict(type='str', default='safe', choices=['all', 'no', 'none', 'safe', 'urllib2', 'yes']),
        creates=dict(type='path'),
        removes=dict(type='path'),
        status_code=dict(type='list', elements='int', default=[200]),
        timeout=dict(type='int', default=30),
        headers=dict(type='dict', default={}),
        unix_socket=dict(type='path'),
        remote_src=dict(type='bool', default=False),
        ca_path=dict(type='path', default=None),
        unredirected_headers=dict(type='list', elements='str', default=[]),
        decompress=dict(type='bool', default=True),
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        add_file_common_args=True,
        mutually_exclusive=[['body', 'src']],
    )

    url = module.params['url']
    body = module.params['body']
    body_format = module.params['body_format'].lower()
    method = module.params['method'].upper()
    dest = module.params['dest']
    return_content = module.params['return_content']
    creates = module.params['creates']
    removes = module.params['removes']
    status_code = [int(x) for x in list(module.params['status_code'])]
    socket_timeout = module.params['timeout']
    ca_path = module.params['ca_path']
    dict_headers = module.params['headers']
    unredirected_headers = module.params['unredirected_headers']
    decompress = module.params['decompress']

    if not re.match('^[A-Z]+$', method):
        module.fail_json(msg="Parameter 'method' needs to be a single word in uppercase, like GET or POST.")

    if body_format == 'json':
        # Encode the body unless its a string, then assume it is pre-formatted JSON
        if not isinstance(body, string_types):
            body = json.dumps(body)
        if 'content-type' not in [header.lower() for header in dict_headers]:
            dict_headers['Content-Type'] = 'application/json'
    elif body_format == 'form-urlencoded':
        if not isinstance(body, string_types):
            try:
                body = form_urlencoded(body)
            except ValueError as e:
                module.fail_json(msg='failed to parse body as form_urlencoded: %s' % to_native(e), elapsed=0)
        if 'content-type' not in [header.lower() for header in dict_headers]:
            dict_headers['Content-Type'] = 'application/x-www-form-urlencoded'
    elif body_format == 'form-multipart':
        try:
            content_type, body = prepare_multipart(body)
        except (TypeError, ValueError) as e:
            module.fail_json(msg='failed to parse body as form-multipart: %s' % to_native(e))
        dict_headers['Content-Type'] = content_type

    if creates is not None:
        # do not run the command if the line contains creates=filename
        # and the filename already exists.  This allows idempotence
        # of uri executions.
        if os.path.exists(creates):
            module.exit_json(stdout="skipped, since '%s' exists" % creates, changed=False)

    if removes is not None:
        # do not run the command if the line contains removes=filename
        # and the filename does not exist.  This allows idempotence
        # of uri executions.
        if not os.path.exists(removes):
            module.exit_json(stdout="skipped, since '%s' does not exist" % removes, changed=False)

    # Make the request
    start = datetime.datetime.utcnow()
    r, info = uri(module, url, dest, body, body_format, method,
                  dict_headers, socket_timeout, ca_path, unredirected_headers,
                  decompress)

    elapsed = (datetime.datetime.utcnow() - start).seconds

    if r and dest is not None and os.path.isdir(dest):
        filename = get_response_filename(r) or 'index.html'
        dest = os.path.join(dest, filename)

    if r and r.fp is not None:
        # r may be None for some errors
        # r.fp may be None depending on the error, which means there are no headers either
        content_type, main_type, sub_type, content_encoding = parse_content_type(r)
    else:
        content_type = 'application/octet-stream'
        main_type = 'aplication'
        sub_type = 'octet-stream'
        content_encoding = 'utf-8'

    maybe_json = content_type and any(candidate in sub_type for candidate in JSON_CANDIDATES)
    maybe_output = maybe_json or return_content or info['status'] not in status_code

    if maybe_output:
        try:
            if PY3 and (r.fp is None or r.closed):
                raise TypeError
            content = r.read()
        except (AttributeError, TypeError):
            # there was no content, but the error read()
            # may have been stored in the info as 'body'
            content = info.pop('body', b'')
    elif r:
        content = r
    else:
        content = None

    resp = {}
    resp['redirected'] = info['url'] != url
    resp.update(info)

    resp['elapsed'] = elapsed
    resp['status'] = int(resp['status'])
    resp['changed'] = False

    # Write the file out if requested
    if r and dest is not None:
        if resp['status'] in status_code and resp['status'] != 304:
            write_file(module, dest, content, resp)
            # allow file attribute changes
            resp['changed'] = True
            module.params['path'] = dest
            file_args = module.load_file_common_arguments(module.params, path=dest)
            resp['changed'] = module.set_fs_attributes_if_different(file_args, resp['changed'])
        resp['path'] = dest

    # Transmogrify the headers, replacing '-' with '_', since variables don't
    # work with dashes.
    # In python3, the headers are title cased.  Lowercase them to be
    # compatible with the python2 behaviour.
    uresp = {}
    for key, value in iteritems(resp):
        ukey = key.replace("-", "_").lower()
        uresp[ukey] = value

    if 'location' in uresp:
        uresp['location'] = absolute_location(url, uresp['location'])

    # Default content_encoding to try
    if isinstance(content, binary_type):
        u_content = to_text(content, encoding=content_encoding)
        if maybe_json:
            try:
                js = json.loads(u_content)
                uresp['json'] = js
            except Exception:
                if PY2:
                    sys.exc_clear()  # Avoid false positive traceback in fail_json() on Python 2
    else:
        u_content = None

    if module.no_log_values:
        uresp = sanitize_keys(uresp, module.no_log_values, NO_MODIFY_KEYS)

    if resp['status'] not in status_code:
        uresp['msg'] = 'Status code was %s and not %s: %s' % (resp['status'], status_code, uresp.get('msg', ''))
        if return_content:
            module.fail_json(content=u_content, **uresp)
        else:
            module.fail_json(**uresp)
    elif return_content:
        module.exit_json(content=u_content, **uresp)
    else:
        module.exit_json(**uresp)
Exemplo n.º 3
0
def main():
    argument_spec = url_argument_spec()
    argument_spec.update(
        dest=dict(type='path'),
        url_username=dict(type='str', aliases=['user']),
        url_password=dict(type='str', aliases=['password'], no_log=True),
        body=dict(type='raw'),
        body_format=dict(
            type='str',
            default='raw',
            choices=['form-urlencoded', 'json', 'raw', 'form-multipart']),
        src=dict(type='path'),
        method=dict(type='str', default='GET'),
        return_content=dict(type='bool', default=False),
        follow_redirects=dict(
            type='str',
            default='safe',
            choices=['all', 'no', 'none', 'safe', 'urllib2', 'yes']),
        creates=dict(type='path'),
        removes=dict(type='path'),
        status_code=dict(type='list', elements='int', default=[200]),
        timeout=dict(type='int', default=30),
        headers=dict(type='dict', default={}),
        unix_socket=dict(type='path'),
        remote_src=dict(type='bool', default=False),
        ca_path=dict(type='path', default=None),
        unredirected_headers=dict(type='list', elements='str', default=[]),
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        add_file_common_args=True,
        mutually_exclusive=[['body', 'src']],
    )

    url = module.params['url']
    body = module.params['body']
    body_format = module.params['body_format'].lower()
    method = module.params['method'].upper()
    dest = module.params['dest']
    return_content = module.params['return_content']
    creates = module.params['creates']
    removes = module.params['removes']
    status_code = [int(x) for x in list(module.params['status_code'])]
    socket_timeout = module.params['timeout']
    ca_path = module.params['ca_path']
    dict_headers = module.params['headers']
    unredirected_headers = module.params['unredirected_headers']

    if not re.match('^[A-Z]+$', method):
        module.fail_json(
            msg=
            "Parameter 'method' needs to be a single word in uppercase, like GET or POST."
        )

    if body_format == 'json':
        # Encode the body unless its a string, then assume it is pre-formatted JSON
        if not isinstance(body, string_types):
            body = json.dumps(body)
        if 'content-type' not in [header.lower() for header in dict_headers]:
            dict_headers['Content-Type'] = 'application/json'
    elif body_format == 'form-urlencoded':
        if not isinstance(body, string_types):
            try:
                body = form_urlencoded(body)
            except ValueError as e:
                module.fail_json(
                    msg='failed to parse body as form_urlencoded: %s' %
                    to_native(e),
                    elapsed=0)
        if 'content-type' not in [header.lower() for header in dict_headers]:
            dict_headers['Content-Type'] = 'application/x-www-form-urlencoded'
    elif body_format == 'form-multipart':
        try:
            content_type, body = prepare_multipart(body)
        except (TypeError, ValueError) as e:
            module.fail_json(msg='failed to parse body as form-multipart: %s' %
                             to_native(e))
        dict_headers['Content-Type'] = content_type

    if creates is not None:
        # do not run the command if the line contains creates=filename
        # and the filename already exists.  This allows idempotence
        # of uri executions.
        if os.path.exists(creates):
            module.exit_json(stdout="skipped, since '%s' exists" % creates,
                             changed=False)

    if removes is not None:
        # do not run the command if the line contains removes=filename
        # and the filename does not exist.  This allows idempotence
        # of uri executions.
        if not os.path.exists(removes):
            module.exit_json(stdout="skipped, since '%s' does not exist" %
                             removes,
                             changed=False)

    # Make the request
    start = datetime.datetime.utcnow()
    resp, content, dest = uri(module, url, dest, body, body_format, method,
                              dict_headers, socket_timeout, ca_path,
                              unredirected_headers)
    resp['elapsed'] = (datetime.datetime.utcnow() - start).seconds
    resp['status'] = int(resp['status'])
    resp['changed'] = False

    # Write the file out if requested
    if dest is not None:
        if resp['status'] in status_code and resp['status'] != 304:
            write_file(module, url, dest, content, resp)
            # allow file attribute changes
            resp['changed'] = True
            module.params['path'] = dest
            file_args = module.load_file_common_arguments(module.params,
                                                          path=dest)
            resp['changed'] = module.set_fs_attributes_if_different(
                file_args, resp['changed'])
        resp['path'] = dest

    # Transmogrify the headers, replacing '-' with '_', since variables don't
    # work with dashes.
    # In python3, the headers are title cased.  Lowercase them to be
    # compatible with the python2 behaviour.
    uresp = {}
    for key, value in iteritems(resp):
        ukey = key.replace("-", "_").lower()
        uresp[ukey] = value

    if 'location' in uresp:
        uresp['location'] = absolute_location(url, uresp['location'])

    # Default content_encoding to try
    content_encoding = 'utf-8'
    if 'content_type' in uresp:
        # Handle multiple Content-Type headers
        charsets = []
        content_types = []
        for value in uresp['content_type'].split(','):
            ct, params = cgi.parse_header(value)
            if ct not in content_types:
                content_types.append(ct)
            if 'charset' in params:
                if params['charset'] not in charsets:
                    charsets.append(params['charset'])

        if content_types:
            content_type = content_types[0]
            if len(content_types) > 1:
                module.warn(
                    'Received multiple conflicting Content-Type values (%s), using %s'
                    % (', '.join(content_types), content_type))
        if charsets:
            content_encoding = charsets[0]
            if len(charsets) > 1:
                module.warn(
                    'Received multiple conflicting charset values (%s), using %s'
                    % (', '.join(charsets), content_encoding))

        u_content = to_text(content, encoding=content_encoding)
        if any(candidate in content_type for candidate in JSON_CANDIDATES):
            try:
                js = json.loads(u_content)
                uresp['json'] = js
            except Exception:
                if PY2:
                    sys.exc_clear(
                    )  # Avoid false positive traceback in fail_json() on Python 2
    else:
        u_content = to_text(content, encoding=content_encoding)

    if module.no_log_values:
        uresp = sanitize_keys(uresp, module.no_log_values, NO_MODIFY_KEYS)

    if resp['status'] not in status_code:
        uresp['msg'] = 'Status code was %s and not %s: %s' % (
            resp['status'], status_code, uresp.get('msg', ''))
        if return_content:
            module.fail_json(content=u_content, **uresp)
        else:
            module.fail_json(**uresp)
    elif return_content:
        module.exit_json(content=u_content, **uresp)
    else:
        module.exit_json(**uresp)
Exemplo n.º 4
0
def test_bad_mime(mocker):
    fields = {'foo': {'filename': 'foo.boom', 'content': 'foo'}}
    mocker.patch('mimetypes.guess_type', side_effect=TypeError)
    content_type, b_data = prepare_multipart(fields)
    assert b'Content-Type: application/octet-stream' in b_data
Exemplo n.º 5
0
def test_unknown_mime(mocker):
    fields = {'foo': {'filename': 'foo.boom', 'content': 'foo'}}
    mocker.patch('mimetypes.guess_type', return_value=(None, None))
    content_type, b_data = prepare_multipart(fields)
    assert b'Content-Type: application/octet-stream' in b_data
Exemplo n.º 6
0
def test_prepare_multipart():
    fixture_boundary = b'===============3996062709511591449=='

    here = os.path.dirname(__file__)
    multipart = os.path.join(here, 'fixtures/multipart.txt')

    client_cert = os.path.join(here, 'fixtures/client.pem')
    client_key = os.path.join(here, 'fixtures/client.key')
    client_txt = os.path.join(here, 'fixtures/client.txt')
    fields = {
        'form_field_1': 'form_value_1',
        'form_field_2': {
            'content': 'form_value_2',
        },
        'form_field_3': {
            'content': '<html></html>',
            'mime_type': 'text/html',
        },
        'form_field_4': {
            'content': '{"foo": "bar"}',
            'mime_type': 'application/json',
        },
        'file1': {
            'content': 'file_content_1',
            'filename': 'fake_file1.txt',
        },
        'file2': {
            'content': '<html></html>',
            'mime_type': 'text/html',
            'filename': 'fake_file2.html',
        },
        'file3': {
            'content': '{"foo": "bar"}',
            'mime_type': 'application/json',
            'filename': 'fake_file3.json',
        },
        'file4': {
            'filename': client_cert,
            'mime_type': 'text/plain',
        },
        'file5': {
            'filename': client_key,
            'mime_type': 'application/octet-stream'
        },
        'file6': {
            'filename': client_txt,
        },
    }

    content_type, b_data = prepare_multipart(fields)

    headers = Message()
    headers['Content-Type'] = content_type
    assert headers.get_content_type() == 'multipart/form-data'
    boundary = headers.get_boundary()
    assert boundary is not None

    with open(multipart, 'rb') as f:
        b_expected = f.read().replace(fixture_boundary, boundary.encode())

    # Depending on Python version, there may or may not be a trailing newline
    assert b_data.rstrip(b'\r\n') == b_expected.rstrip(b'\r\n')