Example #1
0
    def run(self, display_callback):

        log.debug('INFO self.collection_info: %s', self.collection_info)

        # ie, 'v1.2.3.tar.gz', not full path
        archive_filename_basename = \
            ARCHIVE_FILENAME_TEMPLATE.format(namespace=self.collection_info.namespace,
                                             name=self.collection_info.name,
                                             version=self.collection_info.version,
                                             extension=ARCHIVE_FILENAME_EXTENSION)

        archive_path = os.path.join(self.build_context.output_path,
                                    archive_filename_basename)
        log.debug('Building archive into archive_path: %s', archive_path)

        # The name of the top level dir in the tar file, ie, there isnt one.
        archive_top_dir = ""
        log.debug('archive_top_dir: %s', archive_top_dir)

        # 'x:gz' is 'create exclusive gzipped'
        tar_file = tarfile.open(archive_path, mode='w:gz')

        # Find collection files, build a file manifest, serialize to json and add to the tarfile
        file_walker = collection_members.FileWalker(
            collection_path=self.build_context.collection_path)
        col_members = collection_members.CollectionMembers(walker=file_walker)

        log.debug('col_members: %s', col_members)

        col_file_names = col_members.run()
        col_files = collection_artifact_file_manifest.gen_file_manifest_items(
            col_file_names, self.build_context.collection_path)

        file_manifest = CollectionArtifactFileManifest(files=col_files)

        log.debug('file_manifest: %s', file_manifest)

        for col_member_file in file_manifest.files:
            top_dir = False
            # arcname will be a relative path not an abspath at this point
            rel_path = col_member_file.name or col_member_file.src_name
            if rel_path == '.':
                rel_path = ''
            archive_member_path = rel_path

            log.debug('adding %s to %s (from %s)', archive_member_path,
                      archive_path, col_member_file.name)

            log.debug('name=%s, arcname=%s, top_dir=%s', col_member_file.name,
                      archive_member_path, top_dir)

            # if top_dir:
            #     tar_file.add(col_member_file.name, arcname=archive_top_dir, recursive=False)
            # else:
            #     tar_file.add(col_member_file.name, arcname=archive_member_path, recursive=False)
            tar_file.add(col_member_file.src_name,
                         arcname=archive_member_path,
                         recursive=False)

        # Generate FILES.json contents
        # TODO/FIXME: find and use some streamable file format for the filelist (csv?)
        file_manifest_buf = json.dumps(attr.asdict(
            file_manifest, filter=filter_artifact_file_name),
                                       indent=4)

        log.debug('file_manifest_buf: %s', file_manifest_buf)

        b_file_manifest_buf = to_bytes(file_manifest_buf)
        b_file_manifest_buf_bytesio = six.BytesIO(b_file_manifest_buf)

        archive_manifest_path = collection_artifact_manifest.COLLECTION_MANIFEST_FILENAME
        log.debug('archive_manifest_path: %s', archive_manifest_path)

        archive_file_manifest_path = collection_artifact_file_manifest.COLLECTION_FILE_MANIFEST_FILENAME
        log.debug('archive_file_manifest_path: %s', archive_file_manifest_path)

        file_manifest_tar_info = tar_file.gettarinfo(
            os.path.join(self.build_context.collection_path,
                         COLLECTION_INFO_FILENAME))

        file_manifest_tar_info.name = archive_file_manifest_path
        file_manifest_tar_info.size = len(b_file_manifest_buf)

        # Add FILES.json contents to tarball
        tar_file.addfile(tarinfo=file_manifest_tar_info,
                         fileobj=b_file_manifest_buf_bytesio)

        # addfile reads to end of bytesio, seek back to begin
        b_file_manifest_buf_bytesio.seek(0)
        file_manifest_file_chksum = chksums.sha256sum_from_fo(
            b_file_manifest_buf_bytesio)

        log.debug('file_manifest_file_chksum: %s', file_manifest_file_chksum)

        # file_manifest_file_name_in_archive = os.path.relpath(archive_file_manifest_path, self.build_context.collection_path)

        file_manifest_file_item = CollectionArtifactFile(
            src_name=collection_artifact_file_manifest.
            COLLECTION_FILE_MANIFEST_FILENAME,
            # The path where the file will live inside the archive
            name=collection_artifact_file_manifest.
            COLLECTION_FILE_MANIFEST_FILENAME,
            ftype='file',
            chksum_type='sha256',
            chksum_sha256=file_manifest_file_chksum)

        # Generage MANIFEST.json contents
        manifest = CollectionArtifactManifest(
            collection_info=self.collection_info,
            file_manifest_file=file_manifest_file_item)

        log.debug('manifest: %s', manifest)

        manifest_buf = json.dumps(
            attr.asdict(manifest, filter=filter_artifact_file_name),
            # sort_keys=True,
            indent=4)
        log.debug('manifest buf: %s', manifest_buf)

        # add MANIFEST.yml to archive
        b_manifest_buf = to_bytes(manifest_buf)
        b_manifest_buf_bytesio = six.BytesIO(b_manifest_buf)

        archive_manifest_path = os.path.join(
            archive_top_dir,
            collection_artifact_manifest.COLLECTION_MANIFEST_FILENAME)
        log.debug('archive_manifest_path: %s', archive_manifest_path)

        # copy the uid/gid/perms for galaxy.yml to use on the manifes. Need sep instances for manifest and file_manifest
        # TODO: decide on what the generators owner/group/perms should be (root.root 644?)
        manifest_tar_info = tar_file.gettarinfo(
            os.path.join(self.build_context.collection_path,
                         COLLECTION_INFO_FILENAME))

        manifest_tar_info.name = archive_manifest_path
        manifest_tar_info.size = len(b_manifest_buf)

        # TODO: set mtime equal to the 'build time' / build_info when we start creating that.

        tar_file.addfile(tarinfo=manifest_tar_info,
                         fileobj=b_manifest_buf_bytesio)

        log.debug('populated tarfile %s: %s', archive_path,
                  pprint.pformat(tar_file.getmembers))

        tar_file.close()

        # could in theory make creating the release artifact work much the same
        # as serializing some object (I mean, that is what it is... but

        messages = [
            'Building collection: %s' % self.build_context.collection_path,
            'Created  artifact: %s' % archive_path
        ]

        result = BuildResult(
            status=BuildStatuses.success,
            messages=messages,
            # errors=[],
            errors=col_members.walker.file_errors,
            manifest=manifest,
            file_manifest=file_manifest,
            artifact_file_path=archive_path)

        for message in result.messages:
            log.info(message)
            display_callback(message)

        for error in result.errors:
            log.error(error)
            display_callback(error, level='warning')

        return result
Example #2
0
    def run(self, display_callback):

        file_walker = collection_members.FileWalker(collection_path=self.build_context.collection_path)
        col_members = collection_members.CollectionMembers(walker=file_walker)

        log.debug('col_members: %s', col_members)
        log.debug('INFO self.collection_info: %s', self.collection_info)

        col_file_names = col_members.run()
        col_files = collection_artifact_manifest.gen_manifest_artifact_files(col_file_names,
                                                                             self.build_context.collection_path)

        manifest = CollectionArtifactManifest(collection_info=self.collection_info,
                                              files=col_files)

        log.debug('manifest: %s', manifest)

        manifest_buf = json.dumps(attr.asdict(manifest,
                                              filter=filter_artifact_file_name),
                                  # sort_keys=True,
                                  indent=4)
        # manifest_buf = yaml.safe_dump(attr.asdict(manifest),
        #                              default_flow_style=False)
        log.debug('manifest buf: %s', manifest_buf)

        # ie, 'v1.2.3.tar.gz', not full path
        archive_filename_basename = \
            ARCHIVE_FILENAME_TEMPLATE.format(namespace=self.collection_info.namespace,
                                             name=self.collection_info.name,
                                             version=self.collection_info.version,
                                             extension=ARCHIVE_FILENAME_EXTENSION)

        archive_path = os.path.join(self.build_context.output_path,
                                    archive_filename_basename)
        log.debug('Building archive into archive_path: %s', archive_path)

        # The name of the top level dir in the tar file. It is
        # in the format '{collection_name}-{version}'.
        # NOTE: This doesnt follow convention of 'foo-bar-1.2.3.tar.gz -> foo-bar-1.2.3/*'
        archive_top_dir = ARCHIVE_TOPDIR_TEMPLATE.format(collection_info=self.collection_info)

        log.debug('archive_top_dir: %s', archive_top_dir)

        # 'x:gz' is 'create exclusive gzipped'
        tar_file = tarfile.open(archive_path, mode='w:gz')

        # tar_file.add(archive_top_dir, arcname=archive_top_dir, recursive=False)

        for col_member_file in manifest.files:
            top_dir = False
            # arcname will be a relative path not an abspath at this point
            rel_path = col_member_file.name or col_member_file.src_name
            if rel_path == '.':
                rel_path = ''
            archive_member_path = os.path.join(archive_top_dir, rel_path)

            log.debug('adding %s to %s (from %s)', archive_member_path,
                      archive_path, col_member_file.name)

            log.debug('name=%s, arcname=%s, top_dir=%s', col_member_file.name, archive_member_path, top_dir)

            # if top_dir:
            #     tar_file.add(col_member_file.name, arcname=archive_top_dir, recursive=False)
            # else:
            #     tar_file.add(col_member_file.name, arcname=archive_member_path, recursive=False)
            tar_file.add(col_member_file.src_name, arcname=archive_member_path, recursive=False)

        # add MANIFEST.yml to archive

        b_manifest_buf = to_bytes(manifest_buf)
        b_manifest_buf_bytesio = six.BytesIO(b_manifest_buf)

        archive_manifest_path = os.path.join(archive_top_dir,
                                             collection_artifact_manifest.COLLECTION_MANIFEST_FILENAME)
        log.debug('archive_manifest_path: %s', archive_manifest_path)

        # copy the uid/gid/perms for galaxy.yml to use on the manifest
        # TODO: decide on what the generators owner/group/perms should be (root.root 644?)
        manifest_tar_info = tar_file.gettarinfo(os.path.join(self.build_context.collection_path, COLLECTION_INFO_FILENAME))

        manifest_tar_info.name = archive_manifest_path
        manifest_tar_info.size = len(b_manifest_buf)
        # TODO: set mtime equal to the 'build time' / build_info when we start creating that.

        tar_file.addfile(tarinfo=manifest_tar_info,
                         fileobj=b_manifest_buf_bytesio)

        log.debug('populated tarfile %s: %s', archive_path,
                  pprint.pformat(tar_file.getmembers))

        tar_file.close()

        # could in theory make creating the release artifact work much the same
        # as serializing some object (I mean, that is what it is... but

        messages = ['Building collection: %s' % self.build_context.collection_path,
                    'Created  artifact: %s' % archive_path]

        result = BuildResult(status=BuildStatuses.success,
                             messages=messages,
                             # errors=[],
                             errors=col_members.walker.file_errors,
                             manifest=manifest,
                             artifact_file_path=archive_path)

        for message in result.messages:
            log.info(message)
            display_callback(message)

        for error in result.errors:
            log.error(error)
            display_callback(error, level='warning')

        return result
Example #3
0
    def http_request(self, req):
        tmp_ca_cert_path, to_add_ca_cert_path, paths_checked = self.get_ca_certs(
        )
        https_proxy = os.environ.get('https_proxy')
        context = None
        try:
            context = self._make_context(to_add_ca_cert_path)
        except Exception:
            # We'll make do with no context below
            pass

        # Detect if 'no_proxy' environment variable is set and if our URL is included
        use_proxy = self.detect_no_proxy(req.get_full_url())

        if not use_proxy:
            # ignore proxy settings for this host request
            if tmp_ca_cert_path:
                try:
                    os.remove(tmp_ca_cert_path)
                except OSError:
                    pass
            if to_add_ca_cert_path:
                try:
                    os.remove(to_add_ca_cert_path)
                except OSError:
                    pass
            return req

        try:
            if https_proxy:
                proxy_parts = generic_urlparse(urlparse(https_proxy))
                port = proxy_parts.get('port') or 443
                s = socket.create_connection(
                    (proxy_parts.get('hostname'), port))
                if proxy_parts.get('scheme') == 'http':
                    s.sendall(
                        to_bytes(self.CONNECT_COMMAND %
                                 (self.hostname, self.port),
                                 errors='surrogate_or_strict'))
                    if proxy_parts.get('username'):
                        credentials = "%s:%s" % (proxy_parts.get(
                            'username', ''), proxy_parts.get('password', ''))
                        s.sendall(
                            b'Proxy-Authorization: Basic %s\r\n' %
                            base64.b64encode(
                                to_bytes(
                                    credentials,
                                    errors='surrogate_or_strict')).strip())
                    s.sendall(b'\r\n')
                    connect_result = b""
                    while connect_result.find(b"\r\n\r\n") <= 0:
                        connect_result += s.recv(4096)
                        # 128 kilobytes of headers should be enough for everyone.
                        if len(connect_result) > 131072:
                            raise ProxyError(
                                'Proxy sent too verbose headers. Only 128KiB allowed.'
                            )
                    self.validate_proxy_response(connect_result)
                    if context:
                        ssl_s = context.wrap_socket(
                            s, server_hostname=self.hostname)
                    elif HAS_URLLIB3_SSL_WRAP_SOCKET:
                        ssl_s = ssl_wrap_socket(s,
                                                ca_certs=tmp_ca_cert_path,
                                                cert_reqs=ssl.CERT_REQUIRED,
                                                ssl_version=PROTOCOL,
                                                server_hostname=self.hostname)
                    else:
                        ssl_s = ssl.wrap_socket(s,
                                                ca_certs=tmp_ca_cert_path,
                                                cert_reqs=ssl.CERT_REQUIRED,
                                                ssl_version=PROTOCOL)
                        match_hostname(ssl_s.getpeercert(), self.hostname)
                else:
                    raise ProxyError(
                        'Unsupported proxy scheme: %s. Currently ansible only supports HTTP proxies.'
                        % proxy_parts.get('scheme'))
            else:
                s = socket.create_connection((self.hostname, self.port))
                if context:
                    ssl_s = context.wrap_socket(s,
                                                server_hostname=self.hostname)
                elif HAS_URLLIB3_SSL_WRAP_SOCKET:
                    ssl_s = ssl_wrap_socket(s,
                                            ca_certs=tmp_ca_cert_path,
                                            cert_reqs=ssl.CERT_REQUIRED,
                                            ssl_version=PROTOCOL,
                                            server_hostname=self.hostname)
                else:
                    ssl_s = ssl.wrap_socket(s,
                                            ca_certs=tmp_ca_cert_path,
                                            cert_reqs=ssl.CERT_REQUIRED,
                                            ssl_version=PROTOCOL)
                    match_hostname(ssl_s.getpeercert(), self.hostname)
            # close the ssl connection
            # ssl_s.unwrap()
            s.close()
        except (ssl.SSLError, CertificateError) as e:
            build_ssl_validation_error(self.hostname, self.port, paths_checked,
                                       e)
        except socket.error as e:
            raise ConnectionError('Failed to connect to %s at port %s: %s' %
                                  (self.hostname, self.port, to_native(e)))

        try:
            # cleanup the temp file created, don't worry
            # if it fails for some reason
            os.remove(tmp_ca_cert_path)
        except:
            pass

        try:
            # cleanup the temp file created, don't worry
            # if it fails for some reason
            if to_add_ca_cert_path:
                os.remove(to_add_ca_cert_path)
        except:
            pass

        return req
Example #4
0
def open_url(url,
             data=None,
             headers=None,
             method=None,
             use_proxy=True,
             force=False,
             last_mod_time=None,
             timeout=10,
             validate_certs=True,
             url_username=None,
             url_password=None,
             http_agent=None,
             force_basic_auth=False,
             follow_redirects='urllib2',
             client_cert=None,
             client_key=None,
             cookies=None):
    '''
    Sends a request via HTTP(S) or FTP using urllib2 (Python2) or urllib (Python3)

    Does not require the module environment
    '''
    handlers = []
    ssl_handler = maybe_add_ssl_handler(url, validate_certs)
    if ssl_handler:
        handlers.append(ssl_handler)

    parsed = generic_urlparse(urlparse(url))
    if parsed.scheme != 'ftp':
        username = url_username

        if headers is None:
            headers = {}

        if username:
            password = url_password
            netloc = parsed.netloc
        elif '@' in parsed.netloc:
            credentials, netloc = parsed.netloc.split('@', 1)
            if ':' in credentials:
                username, password = credentials.split(':', 1)
            else:
                username = credentials
                password = ''

            parsed_list = parsed.as_list()
            parsed_list[1] = netloc

            # reconstruct url without credentials
            url = urlunparse(parsed_list)

        if username and not force_basic_auth:
            passman = urllib_request.HTTPPasswordMgrWithDefaultRealm()

            # this creates a password manager
            passman.add_password(None, netloc, username, password)

            # because we have put None at the start it will always
            # use this username/password combination for  urls
            # for which `theurl` is a super-url
            authhandler = urllib_request.HTTPBasicAuthHandler(passman)
            digest_authhandler = urllib_request.HTTPDigestAuthHandler(passman)

            # create the AuthHandler
            handlers.append(authhandler)
            handlers.append(digest_authhandler)

        elif username and force_basic_auth:
            headers["Authorization"] = basic_auth_header(username, password)

        else:
            try:
                rc = netrc.netrc(os.environ.get('NETRC'))
                login = rc.authenticators(parsed.hostname)
            except IOError:
                login = None

            if login:
                username, _, password = login
                if username and password:
                    headers["Authorization"] = basic_auth_header(
                        username, password)

    if not use_proxy:
        proxyhandler = urllib_request.ProxyHandler({})
        handlers.append(proxyhandler)

    if HAS_SSLCONTEXT and not validate_certs:
        # In 2.7.9, the default context validates certificates
        context = SSLContext(ssl.PROTOCOL_SSLv23)
        context.options |= ssl.OP_NO_SSLv2
        context.options |= ssl.OP_NO_SSLv3
        context.verify_mode = ssl.CERT_NONE
        context.check_hostname = False
        handlers.append(
            HTTPSClientAuthHandler(client_cert=client_cert,
                                   client_key=client_key,
                                   context=context))
    elif client_cert:
        handlers.append(
            HTTPSClientAuthHandler(client_cert=client_cert,
                                   client_key=client_key))

    # pre-2.6 versions of python cannot use the custom https
    # handler, since the socket class is lacking create_connection.
    # Some python builds lack HTTPS support.
    if hasattr(socket, 'create_connection') and CustomHTTPSHandler:
        handlers.append(CustomHTTPSHandler)

    handlers.append(RedirectHandlerFactory(follow_redirects, validate_certs))

    # add some nicer cookie handling
    if cookies is not None:
        handlers.append(urllib_request.HTTPCookieProcessor(cookies))

    opener = urllib_request.build_opener(*handlers)
    urllib_request.install_opener(opener)

    data = to_bytes(data, nonstring='passthru')
    if method:
        if method.upper() not in ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT',
                                  'DELETE', 'TRACE', 'CONNECT', 'PATCH'):
            raise ConnectionError('invalid HTTP request method; %s' %
                                  method.upper())
        request = RequestWithMethod(url, method.upper(), data)
    else:
        request = urllib_request.Request(url, data)

    # add the custom agent header, to help prevent issues
    # with sites that block the default urllib agent string
    if http_agent:
        request.add_header('User-agent', http_agent)

    # Cache control
    # Either we directly force a cache refresh
    if force:
        request.add_header('cache-control', 'no-cache')
    # or we do it if the original is more recent than our copy
    elif last_mod_time:
        tstamp = last_mod_time.strftime('%a, %d %b %Y %H:%M:%S +0000')
        request.add_header('If-Modified-Since', tstamp)

    # user defined headers now, which may override things we've set above
    if headers:
        if not isinstance(headers, dict):
            raise ValueError("headers provided to fetch_url() must be a dict")
        for header in headers:
            request.add_header(header, headers[header])

    urlopen_args = [request, None]
    if sys.version_info >= (2, 6, 0):
        # urlopen in python prior to 2.6.0 did not
        # have a timeout parameter
        urlopen_args.append(timeout)

    r = urllib_request.urlopen(*urlopen_args)
    return r
Example #5
0
def basic_auth_header(username, password):
    """Takes a username and password and returns a byte string suitable for
    using as value of an Authorization header to do basic auth.
    """
    return b"Basic %s" % base64.b64encode(
        to_bytes("%s:%s" % (username, password), errors='surrogate_or_strict'))