From 1d7b05116db039c31e6ae7e1723c793e8ee64045 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 26 Oct 2020 21:20:19 -0700 Subject: [PATCH] non-working docker provider --- repobot/dockermod.py | 151 +++++++++++++++++++++++++++++++++++++++++++ repobot/server.py | 4 +- 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 repobot/dockermod.py diff --git a/repobot/dockermod.py b/repobot/dockermod.py new file mode 100644 index 0000000..cdf65ec --- /dev/null +++ b/repobot/dockermod.py @@ -0,0 +1,151 @@ +import cherrypy +import hashlib +import json +import os +from jinja2 import Environment, FileSystemLoader, select_autoescape +from sqlalchemy import Column, ForeignKey, UniqueConstraint +from sqlalchemy.orm import relationship +from sqlalchemy.types import String, Integer +from tempfile import TemporaryDirectory +from repobot.tables import Base, db + +""" +Registry API notes + +base URL structure: + +/v2// name matches [a-z0-9]+(?:[._-][a-z0-9]+)* +/v2/library/ubuntu/ including slashes, must be less than 256 chars + +"All endpoints should support aggressive http caching, compression and range headers, where appropriate." +Note: prefixing names with more paths seems to work? +This might be better: https://github.com/cherrypy/tools/blob/master/VirtualHosts + + +4xx errors get a json reply like: + { + "errors:" [{ + "code": , + "message": , + "detail": + }, + ... + ] + } + + + +""" + + +class DockerProvider(object): + def __init__(self, dbcon, s3client, bucket): + self.db = dbcon + self.s3 = s3client + self.bucket = bucket + """base path within the s3 bucket""" + self.basepath = "data/provider/docker" + + cherrypy.tree.mount(DockerApi(self), "/v2/", {'/': {'tools.trailing_slash.on': False, + 'tools.db.on': True}}) + + def web_addpkg(self, reponame, name, version, fobj): + raise NotImplementedError() + + +class DockerApi(object): + def __init__(self, base): + self.base = base + + @cherrypy.expose + def index(self): + cherrypy.response.headers["Docker-Distribution-API-Version"] = "registry/2.0" + return "" + + @cherrypy.expose + def default(self, *args): + # ('foo', 'bar', 'qux', 'dpedu', 'zwastebin2', 'blobs', 'uploads') + # ('dpedu', 'zwastebin2', 'blobs', 'uploads') + # ('ubuntu', 'blobs', 'uploads') + print("##DEFAULT##: ", args) + raise cherrypy.HTTPError(403, "fuck off") + + +@cherrypy.popargs("reponame", "pkgname", "filename") +class TarWeb(object): + def __init__(self, base): + self.base = base + + template_dir = "templates" if os.path.exists("templates") else os.path.join(APPROOT, "templates") + self.tpl = Environment(loader=FileSystemLoader(template_dir), + autoescape=select_autoescape(['html', 'xml'])) + + @cherrypy.expose + def index(self, reponame=None, pkgname=None, filename=None): + if filename: + return self.handle_download(reponame, pkgname, filename) + else: + return self.handle_navigation(reponame, pkgname, filename) + + def handle_navigation(self, reponame=None, pkgname=None, filename=None): + if reponame: + repo = get_repo(db(), reponame, create_ok=False) + if pkgname: + return self.tpl.get_template("tar/package.html") \ + .render(repo=repo, + pkgs=db().query(TarPackage).filter(TarPackage.repo == repo, + TarPackage.name == pkgname). + order_by(TarPackage.version).all()) + + return self.tpl.get_template("tar/repo.html") \ + .render(repo=repo, + pkgs=self._get_dists(repo)) + + return self.tpl.get_template("tar/root.html") \ + .render(repos=db().query(TarRepo).order_by(TarRepo.name).all()) + + def _get_dists(self, repo): + lastpkg = None + for pkg in db().query(TarPackage).filter(TarPackage.repo == repo).order_by(TarPackage.fname).all(): + if lastpkg and pkg.name == lastpkg: + continue + yield pkg + lastpkg = pkg.name + + def handle_download(self, reponame, distname, filename): + repo = get_repo(db(), reponame, create_ok=False) + pkg = db().query(TarPackage).filter(TarPackage.repo == repo, TarPackage.fname == filename).first() + if not pkg: + raise cherrypy.HTTPError(404) + + dpath = os.path.join(self.base.basepath, pkg.blobpath) + print("dpath=", dpath) + print("blobpath=", pkg.blobpath) + print("basepath=", self.base.basepath) + + if str(cherrypy.request.method) == "DELETE": + db().delete(pkg) + files = self.base.s3.list_objects(Bucket=self.base.bucket, Prefix=dpath).get("Contents") + if files: + self.base.s3.delete_object(Bucket=self.base.bucket, Key=dpath) + db().commit() + return "OK" #TODO delete the repo if we've emptied it(?) + + elif str(cherrypy.request.method) == "GET": + response = self.base.s3.get_object(Bucket=self.base.bucket, Key=dpath) + + cherrypy.response.headers["Content-Type"] = "application/octet-stream" + cherrypy.response.headers["Content-Length"] = response["ContentLength"] + + def stream(): + while True: + data = response["Body"].read(65535) + if not data: + return + yield data + + return stream() + else: + raise cherrypy.HTTPError(405) + + index._cp_config = {'response.stream': True} diff --git a/repobot/server.py b/repobot/server.py index 6763f72..a6c474f 100644 --- a/repobot/server.py +++ b/repobot/server.py @@ -7,6 +7,7 @@ from botocore.client import Config as BotoConfig from repobot.aptprovider import AptProvider from repobot.pypiprovider import PypiProvider from repobot.tarprovider import TarProvider +from repobot.dockermod import DockerProvider from repobot.tables import SAEnginePlugin, SATool from urllib.parse import urlparse @@ -80,7 +81,8 @@ def main(): # set up providers providers = {"apt": AptProvider(dbcon, s3, bucket), "pypi": PypiProvider(dbcon, s3, bucket), - "tar": TarProvider(dbcon, s3, bucket)} + "tar": TarProvider(dbcon, s3, bucket), + "docker": DockerProvider(dbcon, s3, bucket)} # set up main web screen web = AppWeb(providers)