-
Notifications
You must be signed in to change notification settings - Fork 25
Support uploading content to pulp #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6d7072d
8d77f7b
19db708
681c117
f5cdc2a
7dd742e
bcffbf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| #!/usr/bin/env python | ||
| import os | ||
| import logging | ||
| from argparse import ArgumentParser | ||
|
|
||
| from pubtools.pulplib import Client | ||
|
|
||
| log = logging.getLogger("upload") | ||
|
|
||
|
|
||
| def upload(client, path, repo_id): | ||
| repo = client.get_repository(repo_id).result() | ||
|
|
||
| uploads = [] | ||
| if os.path.isdir(path): | ||
| for file in os.listdir(path): | ||
| if os.path.isfile(file): | ||
| file = os.path.join(path, file) | ||
| log.debug("Uploading %s to repo %s in threads", file, repo_id) | ||
| uploads.append(repo.upload_file(file)) | ||
| elif os.path.isfile(path): | ||
| log.debug("Uploading %s to repo %s", path, repo_id) | ||
| uploads.append(repo.upload_file(path)) | ||
|
|
||
| for up in uploads: | ||
| result = up.result() | ||
| log.debug("Import task finished:\n%s", result) | ||
|
|
||
| log.info("Uploaded %s files to repository %s", len(uploads), repo_id) | ||
|
|
||
|
|
||
| def make_client(args): | ||
| auth = None | ||
|
|
||
| if args.username: | ||
| password = args.password | ||
| if password is None: | ||
| password = os.environ.get("PULP_PASSWORD") | ||
| if not password: | ||
| log.warning("No password provided for %s", args.username) | ||
| auth = (args.username, args.password) | ||
|
|
||
| return Client(args.url, auth=auth) | ||
|
|
||
|
|
||
| def main(): | ||
| log.setLevel(logging.INFO) | ||
| logging.basicConfig(format="%(message)s", level=logging.INFO) | ||
|
|
||
| parser = ArgumentParser(description="Upload files to Repository") | ||
| parser.add_argument("--url", help="Pulp server URL") | ||
| parser.add_argument("--repo-id", action="store") | ||
| parser.add_argument("--path", action="store", help="Path to a file or a directory") | ||
| parser.add_argument("--username", help="Pulp username") | ||
| parser.add_argument( | ||
| "--password", help="Pulp password (or set PULP_PASSWORD in env)" | ||
| ) | ||
| parser.add_argument("--debug", action="store_true") | ||
|
|
||
| p = parser.parse_args() | ||
|
|
||
| if not p.url: | ||
| parser.error("--url is required") | ||
|
|
||
| if not p.repo_id: | ||
| parser.error("--repo-id is required") | ||
|
|
||
| if not p.path: | ||
| parser.error("--path is required") | ||
|
|
||
| if p.debug: | ||
| logging.getLogger("pubtools.pulplib").setLevel(logging.DEBUG) | ||
JayZ12138 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| log.setLevel(logging.DEBUG) | ||
|
|
||
| client = make_client(p) | ||
| return upload(client, p.path, p.repo_id) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,20 @@ | ||
| import random | ||
| import uuid | ||
| import threading | ||
| import hashlib | ||
|
|
||
| from collections import namedtuple | ||
|
|
||
| import six | ||
| from more_executors.futures import f_return, f_return_error | ||
| from more_executors.futures import f_return, f_return_error, f_flat_map | ||
|
|
||
| from pubtools.pulplib import Page, PulpException, Criteria, Task | ||
| from .. import compat_attr as attr | ||
|
|
||
| from .match import match_object | ||
|
|
||
| Publish = namedtuple("Publish", ["repository", "tasks"]) | ||
| Upload = namedtuple("Upload", ["repository", "tasks", "unit_type_id", "unit_key"]) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is not too good either, exposing the unit_type_id and unit_key here encourages devs to write tests against Pulp2 implementation details where it's not necessary to do so. upload_file is uploading a file "somehow" and the specifics of unit_type_id, unit_key are intentionally hidden, it's undermined by exposing them here like this. If we are adding more attributes here I think they should try to be generic so as to expose as few Pulp2 implementation details as possible, while still meeting the requirements. So, what requirement is being met by these? I think it's probably: allowing the developer of a test to verify that correct content was uploaded to correct path. Then I think it should be done more directly, like exposing "name" and ("sha256" or "content") attributes. If you expose unit_key here, technically developers can look at unit_key["checksum"] to get the checksum. But that kind of implementation detail - "On Pulp, ISO units have a 'checksum' in unit key" - is exactly what this library is meant to be hiding. |
||
|
|
||
|
|
||
| class FakeClient(object): | ||
|
|
@@ -28,6 +30,7 @@ class FakeClient(object): | |
| def __init__(self): | ||
| self._repositories = [] | ||
| self._publish_history = [] | ||
| self._upload_history = [] | ||
| self._lock = threading.RLock() | ||
| self._uuidgen = random.Random() | ||
| self._uuidgen.seed(0) | ||
|
|
@@ -79,6 +82,50 @@ def get_repository(self, repository_id): | |
|
|
||
| return f_return(data[0]) | ||
|
|
||
| def _do_upload_file(self, upload_id, file_obj, name): | ||
| # pylint: disable=unused-argument | ||
| is_file_obj = "close" in dir(file_obj) | ||
| if not is_file_obj: | ||
| file_obj = open(file_obj, "rb") | ||
|
|
||
| def do_next_upload(checksum, size): | ||
| data = file_obj.read(1024 * 1024) | ||
| if data: | ||
| checksum.update(data) | ||
| size += len(data) | ||
| return do_next_upload(checksum, size) | ||
| return f_return((checksum.hexdigest(), size)) | ||
|
|
||
| out = f_flat_map(f_return(), lambda _: do_next_upload(hashlib.sha256(), 0)) | ||
|
|
||
| if not is_file_obj: | ||
| out.add_done_callback(lambda _: file_obj.close()) | ||
|
|
||
| return out | ||
|
|
||
| def _request_upload(self): | ||
| upload_request = { | ||
| "_href": "/pulp/api/v2/content/uploads/%s/" % self._request_id(), | ||
| "upload_id": "%s" % self._request_id, | ||
| } | ||
|
|
||
| return f_return(upload_request) | ||
|
|
||
| def _do_import(self, repo_id, upload_id, unit_type_id, unit_key): | ||
| # pylint: disable=unused-argument | ||
| repo_f = self.get_repository(repo_id) | ||
| if repo_f.exception(): | ||
| # Repo can't be found, let that exception propagate | ||
| return repo_f | ||
|
|
||
| repo = repo_f.result() | ||
|
|
||
| task = Task(id=self._next_task_id(), completed=True, succeeded=True) | ||
|
|
||
| self._upload_history.append(Upload(repo, [task], unit_type_id, unit_key)) | ||
|
|
||
| return f_return([task]) | ||
|
|
||
| def _delete_resource(self, resource_type, resource_id): | ||
| if resource_type == "repositories": | ||
| return self._delete_repository(resource_id) | ||
|
|
@@ -128,3 +175,6 @@ def _next_task_id(self): | |
| with self._lock: | ||
| next_raw_id = self._uuidgen.randint(0, 2 ** 128) | ||
| return str(uuid.UUID(int=next_raw_id)) | ||
|
|
||
| def _request_id(self): | ||
| return self._next_task_id() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,3 +79,22 @@ def publish_history(self): | |
| of this publish | ||
| """ | ||
| return self.client._publish_history[:] | ||
|
|
||
| @property | ||
| def upload_history(self): | ||
| """A list of upload tasks triggered via this client. | ||
|
|
||
| Each element of this list is a named tuple with the following attributes, | ||
| in order: | ||
|
|
||
| ``repository``: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documentation doesn't match reality. The namedtuple you declared actually has a "repo_id" and not a "repository". In this case, I think the documentation is better and the code should be fixed. Publish returns a repository object and not a repo_id, why should Upload be inconsistent with that? Can you please fix that to have a repository object as well?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copy-pasted and wrong :( |
||
| :class:`~pubtools.pulplib.Repository` for which upload was triggered | ||
| ``task``: | ||
| :class:`~pubtools.pulplib.Task` generated as a result | ||
| of this upload | ||
| ``unit_type_id`` (str): | ||
| A string used to indicate the type of uploaded content | ||
| ``unit_key`` (dictionary): | ||
| A dictionary includes information about this upload | ||
| """ | ||
| return self.client._upload_history[:] | ||
Uh oh!
There was an error while loading. Please reload this page.