1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
# Subcommands not exciting enough for their own module.
import argparse
import inspect
import itertools
import os
import os.path
import sys
import charliecloud as ch
import build_cache as bu
import filesystem as fs
import image as im
import pull
import version
## argparse “actions” ##
class Action_Exit(argparse.Action):
def __init__(self, *args, **kwargs):
super().__init__(nargs=0, *args, **kwargs)
class Dependencies(Action_Exit):
def __call__(self, ap, cli, *args, **kwargs):
# ch.init() not yet called, so must get verbosity from arguments.
ch.dependencies_check()
if (cli.verbose >= 1):
print("lark path: %s" % os.path.normpath(inspect.getfile(im.lark)))
sys.exit(0)
class Version(Action_Exit):
def __call__(self, *args, **kwargs):
print(version.VERSION)
sys.exit(0)
## Plain functions ##
# Argument: command line arguments Namespace. Do not need to call sys.exit()
# because caller manages that.
def build_cache(cli):
if (cli.bucache == ch.Build_Mode.DISABLED):
ch.FATAL("build-cache subcommand invalid with build cache disabled")
if (cli.reset):
bu.cache.reset()
if (cli.gc):
bu.cache.garbageinate()
if (cli.tree):
bu.cache.tree_print()
if (cli.dot):
bu.cache.tree_dot()
bu.cache.summary_print()
def delete(cli):
fail_ct = 0
for ref in cli.image_ref:
delete_ct = 0
for img in itertools.chain(im.Image.glob(ref),
im.Image.glob(ref + "_stage[0-9]*")):
bu.cache.unpack_delete(img)
to_delete = im.Reference.ref_to_pathstr(str(img))
bu.cache.branch_delete(to_delete)
delete_ct += 1
if (delete_ct == 0):
fail_ct += 1
ch.ERROR("no matching image, can’t delete: %s" % ref)
bu.cache.worktrees_fix()
if (fail_ct > 0):
ch.FATAL("unable to delete %d invalid image(s)" % fail_ct)
def gestalt_bucache(cli):
bu.have_deps()
def gestalt_bucache_dot(cli):
bu.have_deps()
bu.have_dot()
def gestalt_logging(cli):
ch.TRACE("trace")
ch.DEBUG("debug")
ch.VERBOSE("verbose")
ch.INFO("info")
ch.WARNING("warning")
ch.ERROR("error")
if (cli.fail):
ch.FATAL("the program failed inexplicably")
def gestalt_python_path(cli):
print(sys.executable)
def gestalt_storage_path(cli):
print(ch.storage.root)
def import_(cli):
if (not os.path.exists(cli.path)):
ch.FATAL("can’t copy: not found: %s" % cli.path)
if (ch.xattrs_save):
ch.WARNING("--xattrs unsupported by “ch-image import” (see FAQ)")
pathstr = im.Reference.ref_to_pathstr(cli.image_ref)
if (cli.bucache == ch.Build_Mode.ENABLED):
# Un-tag previously deleted branch, if it exists.
bu.cache.tag_delete(pathstr, fail_ok=True)
dst = im.Image(im.Reference(cli.image_ref))
ch.INFO("importing: %s" % cli.path)
ch.INFO("destination: %s" % dst)
dst.unpack_clear()
if (os.path.isdir(cli.path)):
dst.copy_unpacked(cli.path)
else: # tarball, hopefully
dst.unpack([cli.path])
bu.cache.adopt(dst)
if (dst.metadata["history"] == []):
dst.metadata["history"].append({ "empty_layer": False,
"command": "ch-image import"})
dst.metadata_save()
ch.done_notify()
def list_(cli):
if (cli.undeletable):
# list undeletable images
imgdir = ch.storage.build_cache // "refs/tags"
else:
# list images
imgdir = ch.storage.unpack_base
if (cli.image_ref is None):
# list all images
if (not os.path.isdir(ch.storage.root)):
ch.FATAL("does not exist: %s" % ch.storage.root)
if (not ch.storage.valid_p):
ch.FATAL("not a storage directory: %s" % ch.storage.root)
images = sorted(imgdir.listdir())
if (len(images) >= 1):
img_width = max(len(ref) for ref in images)
for ref in images:
img = im.Image(im.Reference(fs.Path(ref).parts[-1]))
if cli.long:
print("%-*s | %s" % (img_width, img, img.last_modified.ctime()))
else:
print(img)
else:
# list specified image
img = im.Image(im.Reference(cli.image_ref))
print("details of image: %s" % img.ref)
# present locally?
if (not img.unpack_exist_p):
stored = "no"
else:
img.metadata_load()
stored = "yes (%s), modified: %s" % (img.metadata["arch"],
img.last_modified.ctime())
print("in local storage: %s" % stored)
# in cache?
(sid, commit) = bu.cache.find_image(img)
if (sid is None):
cached = "no"
else:
cached = "yes (state ID %s, commit %s)" % (sid.short, commit[:7])
if (os.path.exists(img.unpack_path)):
wdc = bu.cache.worktree_head(img)
if (wdc is None):
ch.WARNING("stored image not connected to build cache")
elif (wdc != commit):
ch.WARNING("stored image doesn’t match build cache: %s" % wdc)
print("in build cache: %s" % cached)
# present remotely?
print("full remote ref: %s" % img.ref.canonical)
pullet = pull.Image_Puller(img, img.ref)
try:
pullet.fatman_load()
remote = "yes"
arch_aware = "yes"
arch_keys = sorted(pullet.architectures.keys())
try:
fmt_space = len(max(arch_keys,key=len))
arch_avail = []
for key in arch_keys:
arch_avail.append("%-*s %s" % (fmt_space, key,
pullet.digests[key][:11]))
except ValueError:
# handles case where arch_keys is empty, e.g.
# mcr.microsoft.com/windows:20H2.
arch_avail = [None]
except ch.Image_Unavailable_Error:
remote = "no (or you are not authorized)"
arch_aware = "n/a"
arch_avail = ["n/a"]
except ch.No_Fatman_Error:
remote = "yes"
arch_aware = "no"
arch_avail = ["unknown"]
pullet.done()
print("available remotely: %s" % remote)
print("remote arch-aware: %s" % arch_aware)
print("host architecture: %s" % ch.arch_host)
print("archs available: %s" % arch_avail[0])
for arch in arch_avail[1:]:
print((" " * 21) + arch)
def reset(cli):
ch.storage.reset()
def undelete(cli):
if (cli.bucache != ch.Build_Mode.ENABLED):
ch.FATAL("only available when cache is enabled")
img = im.Image(im.Reference(cli.image_ref))
if (img.unpack_exist_p):
ch.FATAL("image exists; will not overwrite")
(_, git_hash) = bu.cache.find_deleted_image(img)
if (git_hash is None):
ch.FATAL("image not in cache")
bu.cache.checkout_ready(img, git_hash)
|