From nobody Mon Feb 9 01:16:40 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1563364460; cv=none; d=zoho.com; s=zohoarc; b=h8X51ZUwV6SoSZUQmsVaiJKUWUvPYQAhdVHDKuvQEx1qMM8gG4SuahqSt8GmOvJvmbN1BIsUEs6JYCzyrbVNzkD0wHnyCjQVEO4d0gKurBUzhdCktWw8Ik8UJjsGPGbFhT+ND//oSpO7QKE0VBu6zkiN0iyKKfcCKBe+CL5Va3U= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1563364460; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=VeMqTiLobLabiL1T5PAiBs/qMhZFn3Wws3pTTFMzvyI=; b=QgVW1wv2TlzVNp2KPZiFYiD4gVJHleWUmFs0TRXXT6OtpW/fbm5y7/R5zgI3wWyvC1Hlgu9LvOcb7ZLW8dfqiD1Q/ENi4pxqPW/FtLqhURricaZAdxZJFyVPLu9mpPaA2UB/0Mz0viecRudRIOVJZSYN4yTJpd6aMsziPzQVGdg= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1563364460732399.7892905444063; Wed, 17 Jul 2019 04:54:20 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6DE7B335C1; Wed, 17 Jul 2019 11:54:18 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id DB5B25B685; Wed, 17 Jul 2019 11:54:17 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 2D1C018045CE; Wed, 17 Jul 2019 11:54:16 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x6HBsEqf031349 for ; Wed, 17 Jul 2019 07:54:14 -0400 Received: by smtp.corp.redhat.com (Postfix) id 835E91001B27; Wed, 17 Jul 2019 11:54:14 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.73]) by smtp.corp.redhat.com (Postfix) with ESMTPS id DB9FE1001B00 for ; Wed, 17 Jul 2019 11:54:13 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Wed, 17 Jul 2019 13:53:50 +0200 Message-Id: <20190717115409.1452-2-abologna@redhat.com> In-Reply-To: <20190717115409.1452-1-abologna@redhat.com> References: <20190717115409.1452-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-loop: libvir-list@redhat.com Subject: [libvirt] [jenkins-ci PATCH 01/20] quayadmin: Import initial implementation X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Wed, 17 Jul 2019 11:54:18 +0000 (UTC) This code was written by Daniel P. Berrang=C3=A9, and contains the scaffolding necessary to call Quay APIs as well as the implementation of a few commands. Signed-off-by: Andrea Bolognani --- guests/quayadmin | 198 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100755 guests/quayadmin diff --git a/guests/quayadmin b/guests/quayadmin new file mode 100755 index 0000000..31ea929 --- /dev/null +++ b/guests/quayadmin @@ -0,0 +1,198 @@ +#!/usr/bin/python3 +# -*- python -*- +# +# quayadmin - client for quay.io +# +# Copyright (C) 2019 Red Hat, Inc. +# +# 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 2 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 . + +import argparse +import requests +import sys + +baseurl =3D "https://quay.io/api/v1" +clientid =3D "xxx" +clientsecret =3D "xxx" +token=3D "xxx" + + +def request(endpoint, method, payload=3DNone, params=3DNone): + url =3D baseurl + endpoint + + headers =3D { + "Authorization": "Bearer {}".format(token) + } + + return method(url, headers=3Dheaders, json=3Dpayload, params=3Dparams) + + +def get(endpoint, params=3DNone): + return request(endpoint, method=3Drequests.get, params=3Dparams) + + +def delete(endpoint, payload=3DNone): + return request(endpoint, method=3Drequests.delete, payload=3Dpayload) + + +def post(endpoint, payload=3DNone): + return request(endpoint, method=3Drequests.post, payload=3Dpayload) + + +def has_error(quiet, res, expected, message): + if res.status_code =3D=3D expected: + return False + + if res.status_code >=3D 400 and res.status_code < 500: + info =3D res.json() + err =3D info["error_message"] + else: + err =3D res.content + + if not quiet: + print("{}: {} ({})".format(message, err, res.status_code)) + return True + + +def run_list_repos(args): + res =3D get("/repository", params=3D{"namespace": args.namespace}) + + if has_error(args.quiet, res, 200, "Cannot list repositories"): + return 1 + + info =3D res.json() + for repo in info["repositories"]: + print ("{}/{}".format(repo["namespace"], repo["name"])) + + +def run_show_repo(args): + res =3D get("/repository/{}/{}".format(args.namespace, args.repo)) + + if has_error(args.quiet, res, 200, "Cannot query repository {}/{}" + .format(args.namespace, args.repo)): + return 1 + + info =3D res.json() + if not args.quiet: + print("{}/{}: {}".format(args.namespace, args.repo, info["descript= ion"])) + + +def run_create_repo(args): + res =3D post("/repository", payload=3D{ + "repo_kind": "image", + "namespace": args.namespace, + "visibility": "public", + "repository": args.repo, + "description": args.desc, + }) + + if has_error(args.quiet, res, 201, "Cannot create repository {}/{}" + .format(args.namespace, args.repo)): + return 1 + + if not args.quiet: + print("Repository {}/{} created".format(args.namespace, args.repo)) + + +def run_delete_repo(args): + res =3D delete("/repository/{}/{}".format(args.namespace, args.repo)) + + if has_error(args.quiet, res, 204, "Cannot delete repository {}/{}" + .format(args.namespace, args.repo)): + return 1 + + if not args.quiet: + print("Repository {}/{} deleted".format(args.namespace, args.repo)) + + +def add_arg_namespace(parser): + parser.add_argument("namespace", help=3D"Organization or user name") + + +def add_arg_repo(parser): + parser.add_argument("repo", help=3D"Repository name") + + +def add_arg_desc(parser): + parser.add_argument("desc", help=3D"Repository description") + + +def build_parser_list_repos(subparser): + parser =3D subparser.add_parser("list-repos", help=3D"List container r= epositories") + + parser.set_defaults(func=3Drun_list_repos) + + add_arg_namespace(parser) + + +def build_parser_create_repo(subparser): + parser =3D subparser.add_parser("create-repo", help=3D"Create a new re= pository") + + parser.set_defaults(func=3Drun_create_repo) + + add_arg_namespace(parser) + add_arg_repo(parser) + add_arg_desc(parser) + + +def build_parser_show_repo(subparser): + parser =3D subparser.add_parser("show-repo", help=3D"Show repository i= nfo") + + parser.set_defaults(func=3Drun_show_repo) + + add_arg_namespace(parser) + add_arg_repo(parser) + + +def build_parser_delete_repo(subparser): + parser =3D subparser.add_parser("delete-repo", help=3D"Delete an exist= ing repository") + + parser.set_defaults(func=3Drun_create_repo) + + add_arg_namespace(parser) + add_arg_repo(parser) + add_arg_desc(parser) + + +def build_parser(): + parser =3D argparse.ArgumentParser( + description=3D"quay.io client admin tool" + ) + + parser.add_argument("--debug", '-d', action=3D"store_true", help=3D"Pr= int debugging information") + parser.add_argument("--quiet", '-q', action=3D"store_true", help=3D"Di= splay minimal information") + + subparser =3D parser.add_subparsers(metavar=3D"COMMAND") + subparser.required =3D True + + build_parser_list_repos(subparser) + build_parser_show_repo(subparser) + build_parser_create_repo(subparser) + build_parser_delete_repo(subparser) + + return parser + +def main(): + parser =3D build_parser() + args =3D parser.parse_args() + + try: + res =3D args.func(args) + sys.exit(res) + except Exception as ex: + sys.stderr.write("{}: {}\n".format(sys.argv[0], ex)) + sys.exit(1) + +if __name__ =3D=3D "__main__": + main() --=20 2.21.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list