pakfire_PYTHON = \
src/pakfire/__init__.py \
src/pakfire/__version__.py \
- src/pakfire/client.py \
src/pakfire/config.py \
src/pakfire/constants.py \
src/pakfire/daemon.py \
+++ /dev/null
-#!/usr/bin/python3
-###############################################################################
-# #
-# Pakfire - The IPFire package management system #
-# Copyright (C) 2013 Pakfire development team #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import logging
-
-from . import config
-from . import hub
-
-from .constants import *
-from .i18n import _
-
-log = logging.getLogger("pakfire.client")
-log.propagate = 1
-
-class Client(object):
- def __init__(self, config_file="client.conf"):
- # Read configuration
- self.config = config.Config(config_file)
-
- # Create connection to the hub
- self.hub = self.connect_to_hub()
-
- def connect_to_hub(self):
- huburl = self.config.get("client", "server", PAKFIRE_HUB)
-
- # keytab
- keytab = self.config.get("client", "keytab")
-
- # Create a connection to the hub
- return hub.Hub(huburl, keytab=keytab)
-
- async def check_connection(self):
- """
- Tests the connection to the hub
- """
- return await self.hub.test()
-
- # Builds
-
- async def build(self, *args, **kwargs):
- return await self.hub.build(*args, **kwargs)
-
- def get_build(self, build_id):
- return Build(self, build_id)
-
- # Jobs
-
- def get_job(self, job_id):
- return Job(self, job_id)
-
- # Uploads
-
- def upload(self, *args, **kwargs):
- return self.hub.upload(*args, **kwargs)
-
-
-class _ClientObject(object):
- def __init__(self, client, id):
- self.client = client
-
- # UUID of the build
- self.id = id
-
- self._data = None
- self._cache = {}
-
- def __repr__(self):
- return "<%s %s>" % (self.__class__.__name__, self.id)
-
- def __eq__(self, other):
- return self.id == other.id
-
- @property
- def data(self):
- if self._data is None:
- self._data = self._load()
-
- return self._data
-
- def refresh(self):
- self._data = self._load()
-
- def _load(self):
- """
- Loads information about this object from the hub
- """
- raise NotImplementedError
-
-
-class Build(_ClientObject):
- def _load(self):
- """
- Loads information about this build from the hub
- """
- return self.client.hub.get_build(self.id)
-
- @property
- def jobs(self):
- jobs = []
-
- for job in self.data.get("jobs"):
- try:
- j = self._cache[job]
- except KeyError:
- j = Job(self.client, job)
-
- jobs.append(j)
-
- return sorted(jobs)
-
- @property
- def type(self):
- """
- The type of this build (release or scratch)
- """
- return self.data.get("type")
-
- @property
- def _type_tag(self):
- if self.type == "release":
- return "[R]"
-
- elif self.type == "scratch":
- return "[S]"
-
- @property
- def name(self):
- """
- The name of this build
- """
- return self.data.get("name")
-
- @property
- def priority(self):
- """
- The priority of this build
- """
- return self.data.get("priority")
-
- @property
- def score(self):
- """
- The score of this build
- """
- return self.data.get("score")
-
- @property
- def state(self):
- """
- The state this build is in
- """
- return self.data.get("state")
-
- def is_active(self):
- return self.state == "building"
-
- @property
- def oneline(self):
- s = [
- self.name,
- self._type_tag,
- "(%s)" % self.id,
- _("Score: %s") % self.score,
- _("Priority: %s") % self.priority,
- ]
-
- return " ".join(s)
-
- def dump(self):
- # Add a headline for the build
- s = [
- self.oneline,
- ]
-
- # Add a short line for each job
- for j in self.jobs:
- s.append(" %s" % j.oneline)
-
- return "\n".join(s)
-
-
-class Job(_ClientObject):
- def _load(self):
- """
- Loads information about this job from the hub
- """
- return self.client.hub.get_job(self.id)
-
- def __lt__(self, other):
- return self.arch < other.arch
-
- @property
- def name(self):
- """
- The name of this job
- """
- return self.data.get("name")
-
- @property
- def type(self):
- """
- The type of this build
- """
- return self.data.get("type")
-
- @property
- def arch(self):
- """
- The architecture of this job
- """
- return self.data.get("arch")
-
- @property
- def builder(self):
- """
- The name of the builder that built this job
- """
- return self.data.get("builder")
-
- @property
- def duration(self):
- """
- The duration the job took to build
- """
- return self.data.get("duration")
-
- @property
- def state(self):
- """
- The state of this job
- """
- return self.data.get("state")
-
- def is_active(self):
- return self.state in ("dispatching", "running", "uploading")
-
- def is_finished(self):
- return self.state == "finished"
-
- @property
- def oneline(self):
- s = [
- # Architecture
- "[%-8s]" % self.arch,
-
- # State
- "[%12s]" % self.state,
- ]
-
- if self.is_active():
- s.append(_("on %s") % self.builder)
-
- elif self.is_finished():
- s.append(_("in %s") % ("%02d:%02d" % (self.duration // 60, self.duration % 60)))
-
- return " ".join(s)
-
- def dump(self):
- s = []
-
- # Name
- s.append(self.name)
-
- # Append tag for test buils
- if self.type == "test":
- s.append("[T]")
-
- # UUID
- s.append("(%s)" % self.id)
-
- # Show the builder for active jobs
- if self.is_active() and self.builder:
- s.append(_("Builder: %s") % self.builder)
-
- return " ".join(s)
query_args = urllib.parse.urlencode(kwargs)
# Add query arguments
- if method in ("GET", "PUT"):
+ if method in ("GET", "PUT", "DELETE"):
url = "%s?%s" % (url, query_args)
# Add any arguments to the body
def get_build(self, uuid):
return self._request("/builds/%s" % uuid, decode="json")
- async def build(self, path, arches=None):
+ async def build(self, path, repo=None, arches=None):
"""
Create a new build on the hub
"""
# Create a new build
build_id = await self._request("POST", "/builds",
- upload_id=upload_id, arches=arches)
+ upload_id=upload_id, repo=repo, arches=arches)
log.debug("Build creates as %s" % build_id)
def get_package(self, uuid):
return self._request("/packages/%s" % uuid, decode="json")
- # File uploads
+ # Uploads
+
+ async def list_uploads(self):
+ """
+ Returns a list of all uploads
+ """
+ response = await self._request("GET", "/uploads")
+
+ return response.get("uploads")
async def upload(self, path, filename=None, show_progress=True):
"""
# Return the upload ID
return response.get("id")
+ async def delete_upload(self, upload_id):
+ await self._request("DELETE", "/uploads", id=upload_id)
+
@staticmethod
def _stream_file(path, size, p, write):
# Start the progressbar
import asyncio
import argparse
-import os.path
import sys
import tempfile
-import time
-import pakfire.client
+import pakfire.config
+import pakfire.hub
+
from pakfire.i18n import _
class Cli(object):
+ def __init__(self):
+ # Read configuration
+ self.config = pakfire.config.Config("client.conf")
+
def parse_cli(self):
parser = argparse.ArgumentParser(
description = _("Pakfire Client command line interface"),
help=_("Package(s) to build"))
build.add_argument("-a", "--arch",
help=_("Build the package(s) for the given architecture only"))
+ build.add_argument("--repo",
+ help=_("Create the build in this repository"))
build.set_defaults(func=self._build)
# check-connection
# upload
upload = subparsers.add_parser("upload",
help=_("Upload a file to the build service"))
- upload.add_argument("file", nargs="+",
- help=_("Filename"))
- upload.set_defaults(func=self._upload)
-
- # watch-build
- watch_build = subparsers.add_parser("watch-build",
- help=_("Watch the status of a build"))
- watch_build.add_argument("id",
- help=_("Build ID"))
- watch_build.set_defaults(func=self._watch_build)
-
- # watch-job
- watch_job = subparsers.add_parser("watch-job",
- help=_("Watch the status of a job"))
- watch_job.add_argument("id",
- help=_("Job ID"))
- watch_job.set_defaults(func=self._watch_job)
+
+ upload_subparsers = upload.add_subparsers()
+
+ # upload list
+ upload_list = upload_subparsers.add_parser("list", help=_("List all uploads"))
+ upload_list.set_defaults(func=self._upload_list)
+
+ # upload new
+ upload_new = upload_subparsers.add_parser("new", help=_("Create a new upload"))
+ upload_new.add_argument("file", nargs="+", help=_("Filename"))
+ upload_new.set_defaults(func=self._upload_new)
+
+ # upload delete
+ upload_delete = upload_subparsers.add_parser("delete", help=_("Delete an upload"))
+ upload_delete.add_argument("upload_id", metavar="ID", nargs="+",
+ help=_("One or multiple IDs"))
+ upload_delete.set_defaults(func=self._upload_delete)
args = parser.parse_args()
# Parse command line arguments
args = self.parse_cli()
- # Create the Client
- client = pakfire.client.Client()
+ # Create connection to the hub
+ hub = self.connect_to_hub()
+
+ # Setup the callback
+ callback = args.func(hub, args)
# Call function
try:
- ret = asyncio.run(args.func(client, args))
+ ret = asyncio.run(callback)
# Catch invalid inputs
except ValueError as e:
# Return with exit code
sys.exit(ret or 0)
- async def _build(self, client, ns):
+ def connect_to_hub(self):
+ """
+ Establishes a connection to the hub
+ """
+ huburl = self.config.get("client", "server", "https://pakfirehub.ipfire.org")
+
+ # keytab
+ keytab = self.config.get("client", "keytab")
+
+ # Create a connection to the hub
+ return pakfire.hub.Hub(huburl, keytab=keytab)
+
+ async def _build(self, hub, ns):
# Create a temporary directory if we need to call dist
tmp = tempfile.TemporaryDirectory(prefix="pakfire-client-")
# Build all packages
for package in ns.packages:
- await client.build(package, arches=ns.arch)
+ await hub.build(package, arches=ns.arch, repo=ns.repo)
finally:
tmp.cleanup()
- async def _check_connection(self, client, ns):
- response = await client.check_connection()
+ async def _check_connection(self, hub, ns):
+ response = await hub.check_connection()
# Print the response from the service
try:
except KeyError:
pass
- async def _upload(self, client, ns):
+ # Uploads
+
+ async def _upload_list(self, hub, ns):
+ uploads = await hub.list_uploads()
+
+ for upload in uploads:
+ print(_("Upload %s") % upload.get("id"))
+
+ attributes = {
+ _("Filename") : upload.get("filename"),
+ _("Size") : upload.get("size"),
+ _("Created at") : upload.get("created_at"),
+ _("Expires at") : upload.get("expires_at"),
+ }
+
+ for key, val in attributes.items():
+ print (" %-20s : %s" % (key, val))
+
+ print() # newline
+
+ async def _upload_new(self, hub, ns):
for file in ns.file:
- upload_id = await client.upload(file)
+ upload_id = await hub.upload(file)
# Tell the user
print(_("%(file)s uploaded as %(id)s") % {
}
)
- async def _watch_build(self, client, ns):
- build = client.get_build(ns.id[0])
-
- return self._watch_something(build)
-
- async def _watch_job(self, client, ns):
- job = client.get_job(ns.id[0])
-
- return self._watch_something(job)
-
- async def _watch_something(self, o):
- while True:
- s = o.dump()
- print(s)
-
- # Break the loop if the build/job is not active any more
- # (since we don't expect any changes)
- if not o.is_active():
- break
-
- time.sleep(60)
+ async def _upload_delete(self, hub, ns):
+ """
+ Delete uploads
+ """
+ for upload_id in ns.upload_id:
+ await hub.delete_upload(upload_id)
- # Update data before the next loop is shown
- o.refresh()
+ print(_("Deleted upload %s") % upload_id)
if __name__ == "__main__":