You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
7.2 KiB
216 lines
7.2 KiB
6 years ago
|
#! /usr/bin/python
|
||
|
|
||
|
# Copyright 2013-2019 Canonical Ltd.
|
||
|
# Author: Colin Watson <cjwatson@ubuntu.com>
|
||
|
|
||
|
# 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; version 3 of the License.
|
||
|
#
|
||
|
# 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/>.
|
||
|
|
||
|
"""Manage build base images."""
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
__metaclass__ = type
|
||
|
|
||
|
import argparse
|
||
|
import hashlib
|
||
|
import subprocess
|
||
|
import sys
|
||
|
try:
|
||
|
from urllib.parse import urlparse
|
||
|
except ImportError:
|
||
|
from urlparse import urlparse
|
||
|
|
||
|
from launchpadlib.launchpad import Launchpad
|
||
|
from launchpadlib.uris import web_root_for_service_root
|
||
|
from ubuntutools.question import YesNoQuestion
|
||
|
|
||
|
import lputils
|
||
|
|
||
|
|
||
|
# Convenience aliases.
|
||
|
image_types = {
|
||
|
"chroot": "Chroot tarball",
|
||
|
"lxd": "LXD image",
|
||
|
}
|
||
|
|
||
|
|
||
|
def describe_image_type(image_type):
|
||
|
if image_type == "Chroot tarball":
|
||
|
return "base chroot tarball"
|
||
|
elif image_type == "LXD image":
|
||
|
return "base LXD image"
|
||
|
else:
|
||
|
raise ValueError("unknown image type '%s'" % image_type)
|
||
|
|
||
|
|
||
|
def get_chroot(args):
|
||
|
das = args.architectures[0]
|
||
|
suite_arch = "%s/%s" % (args.suite, das.architecture_tag)
|
||
|
url = das.getChrootURL(pocket=args.pocket, image_type=args.image_type)
|
||
|
if url is None:
|
||
|
print("No %s for %s" % (
|
||
|
describe_image_type(args.image_type), suite_arch))
|
||
|
return 1
|
||
|
if args.dry_run:
|
||
|
print("Would fetch %s" % url)
|
||
|
else:
|
||
|
# We use wget here to save on having to implement a progress bar
|
||
|
# with urlretrieve.
|
||
|
command = ["wget"]
|
||
|
if args.filepath is not None:
|
||
|
command.extend(["-O", args.filepath])
|
||
|
command.append(url)
|
||
|
subprocess.check_call(command)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def info_chroot(args):
|
||
|
das = args.architectures[0]
|
||
|
url = das.getChrootURL(pocket=args.pocket, image_type=args.image_type)
|
||
|
if url is not None:
|
||
|
print(url)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def remove_chroot(args):
|
||
|
das = args.architectures[0]
|
||
|
previous_url = das.getChrootURL(
|
||
|
pocket=args.pocket, image_type=args.image_type)
|
||
|
if previous_url is not None:
|
||
|
print("Previous %s: %s" % (
|
||
|
describe_image_type(args.image_type), previous_url))
|
||
|
suite_arch = "%s/%s" % (args.suite, das.architecture_tag)
|
||
|
if args.dry_run:
|
||
|
print("Would remove %s from %s" % (
|
||
|
describe_image_type(args.image_type), suite_arch))
|
||
|
else:
|
||
|
if not args.confirm_all:
|
||
|
if YesNoQuestion().ask(
|
||
|
"Remove %s from %s" % (
|
||
|
describe_image_type(args.image_type), suite_arch),
|
||
|
"no") == "no":
|
||
|
return 0
|
||
|
das.removeChroot(pocket=args.pocket, image_type=args.image_type)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def set_chroot(args):
|
||
|
das = args.architectures[0]
|
||
|
previous_url = das.getChrootURL(
|
||
|
pocket=args.pocket, image_type=args.image_type)
|
||
|
if previous_url is not None:
|
||
|
print("Previous %s: %s" % (
|
||
|
describe_image_type(args.image_type), previous_url))
|
||
|
suite_arch = "%s/%s" % (args.suite, das.architecture_tag)
|
||
|
if args.build_url:
|
||
|
target = "%s from %s" % (args.filepath, args.build_url)
|
||
|
else:
|
||
|
target = args.filepath
|
||
|
if args.dry_run:
|
||
|
print("Would set %s for %s to %s" % (
|
||
|
describe_image_type(args.image_type), suite_arch, target))
|
||
|
else:
|
||
|
if not args.confirm_all:
|
||
|
if YesNoQuestion().ask(
|
||
|
"Set %s for %s to %s" % (
|
||
|
describe_image_type(args.image_type), suite_arch, target),
|
||
|
"no") == "no":
|
||
|
return 0
|
||
|
if args.build_url:
|
||
|
das.setChrootFromBuild(
|
||
|
livefsbuild=urlparse(args.build_url).path,
|
||
|
filename=args.filepath,
|
||
|
pocket=args.pocket, image_type=args.image_type)
|
||
|
else:
|
||
|
with open(args.filepath, "rb") as f:
|
||
|
data = f.read()
|
||
|
sha1sum = hashlib.sha1(data).hexdigest()
|
||
|
das.setChroot(
|
||
|
data=data, sha1sum=sha1sum,
|
||
|
pocket=args.pocket, image_type=args.image_type)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
commands = {
|
||
|
"get": get_chroot,
|
||
|
"info": info_chroot,
|
||
|
"remove": remove_chroot,
|
||
|
"set": set_chroot}
|
||
|
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument(
|
||
|
"-l", "--launchpad", dest="launchpad_instance", default="production")
|
||
|
parser.add_argument(
|
||
|
"-n", "--dry-run", default=False, action="store_true",
|
||
|
help="only show removals that would be performed")
|
||
|
parser.add_argument(
|
||
|
"-y", "--confirm-all", default=False, action="store_true",
|
||
|
help="do not ask for confirmation")
|
||
|
parser.add_argument(
|
||
|
"-d", "--distribution", default="ubuntu",
|
||
|
metavar="DISTRIBUTION", help="manage base images for DISTRIBUTION")
|
||
|
parser.add_argument(
|
||
|
"-s", "--suite", "--series", dest="suite", metavar="SUITE",
|
||
|
help="manage base images for SUITE")
|
||
|
parser.add_argument(
|
||
|
"-a", "--architecture", metavar="ARCHITECTURE", required=True,
|
||
|
help="manage base images for ARCHITECTURE")
|
||
|
parser.add_argument(
|
||
|
"-i", "--image-type", metavar="TYPE", default="Chroot tarball",
|
||
|
help="manage base images of type TYPE")
|
||
|
parser.add_argument(
|
||
|
"--from-build", dest="build_url", metavar="URL",
|
||
|
help="Live filesystem build URL to set base image from")
|
||
|
parser.add_argument(
|
||
|
"-f", "--filepath", metavar="PATH",
|
||
|
help="Base image file path (or file name if --from-build is given)")
|
||
|
parser.add_argument("command", choices=sorted(commands.keys()))
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
if args.command == "set" and args.filepath is None:
|
||
|
parser.error("The set command requires a base image file path (-f).")
|
||
|
|
||
|
if args.image_type not in image_types.values():
|
||
|
image_type = image_types.get(args.image_type.lower())
|
||
|
if image_type is not None:
|
||
|
args.image_type = image_type
|
||
|
else:
|
||
|
parser.error("Unknown image type '%s'." % args.image_type)
|
||
|
|
||
|
if args.command in ("get", "info"):
|
||
|
login_method = Launchpad.login_anonymously
|
||
|
else:
|
||
|
login_method = Launchpad.login_with
|
||
|
args.launchpad = login_method(
|
||
|
"manage-chroot", args.launchpad_instance, version="devel")
|
||
|
lputils.setup_location(args)
|
||
|
|
||
|
if args.command == "set" and args.build_url:
|
||
|
parsed_build_url = urlparse(args.build_url)
|
||
|
if parsed_build_url.scheme != "":
|
||
|
service_host = args.launchpad._root_uri.host
|
||
|
web_host = urlparse(web_root_for_service_root(
|
||
|
str(args.launchpad._root_uri))).hostname
|
||
|
if parsed_build_url.hostname not in (service_host, web_host):
|
||
|
parser.error(
|
||
|
"%s is not on this Launchpad instance (%s)" % (
|
||
|
args.build_url, web_host))
|
||
|
|
||
|
return commands[args.command](args)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
sys.exit(main())
|