Skip to content

Commit e812014

Browse files
jlebonPeaceRebel
authored andcommitted
Add cosa import
This command takes as argument a `containers-transport(5)`-style pullspec and creates a new cosa build dir from it. It essentially bridges the gap between coreos/fedora-coreos-config#3348 and the rest of the cosa pipeline. co-authored by: Bipin B Narayan <bbnaraya@redhat.com>
1 parent 574b0e2 commit e812014

2 files changed

Lines changed: 184 additions & 1 deletion

File tree

cmd/coreos-assembler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var advancedBuildCommands = []string{"buildfetch", "buildupload", "oc-adm-releas
1717
var buildextendCommands = []string{"aliyun", "applehv", "aws", "azure", "digitalocean", "exoscale", "extensions-container", "gcp", "hyperv", "ibmcloud", "kubevirt", "live", "metal", "metal4k", "nutanix", "openstack", "oraclecloud", "qemu", "secex", "virtualbox", "vmware", "vultr"}
1818

1919
var utilityCommands = []string{"aws-replicate", "coreos-prune", "compress", "copy-container", "diff", "koji-upload", "kola", "push-container-manifest", "remote-build-container", "remote-session", "sign", "tag", "update-variant"}
20-
var otherCommands = []string{"shell", "meta"}
20+
var otherCommands = []string{"import", "shell", "meta"}
2121

2222
func init() {
2323
// Note buildCommands is intentionally listed in frequency order

src/cmd-import

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/python3
2+
3+
import argparse
4+
import json
5+
import os
6+
import subprocess
7+
import tempfile
8+
import shutil
9+
import sys
10+
from stat import (
11+
S_IREAD,
12+
S_IRGRP,
13+
S_IROTH)
14+
from cosalib.builds import Builds
15+
from cosalib.cmdlib import (
16+
import_ostree_commit,
17+
get_basearch,
18+
sha256sum_file)
19+
20+
VALID_NAMES = ["fedora-coreos", "rhcos", "scos"]
21+
22+
23+
def main():
24+
args = parse_args()
25+
with tempfile.TemporaryDirectory(prefix='cosa-import-', dir='tmp') as tmpd:
26+
populate_build_dir(args, tmpd)
27+
# XXX: for debugging until we're ready
28+
print("Temporary directory is:", tmpd)
29+
sys.exit()
30+
31+
32+
def parse_args():
33+
parser = argparse.ArgumentParser(prog='cosa import')
34+
parser.add_argument("--rechunk", action='store_true', help="rechunk image")
35+
parser.add_argument("srcimg", metavar='IMAGE',
36+
help="image to import (containers-transports(5) format)")
37+
parser.add_argument("--name", help="name to be set for the imported image. (eg: fedora-coreos)")
38+
return parser.parse_args()
39+
40+
def extract_name(args):
41+
if args.name:
42+
if args.name in VALID_NAMES:
43+
return args.name
44+
raise Exception(f'{args.name} is not a valid name')
45+
46+
for name in VALID_NAMES:
47+
if name in args.srcimg:
48+
return name
49+
50+
raise Exception("unable to guess name for the image. Use --name option to set a name")
51+
52+
53+
def prepare_build(builds, buildid):
54+
pwd = os.getcwd()
55+
builddir = builds.get_build_dir(buildid)
56+
buildmeta = builds.get_build_meta(buildid)
57+
import_ostree_commit(pwd, builddir, buildmeta, extract_json=0)
58+
59+
60+
def populate_build_dir(args, tmpd):
61+
name = extract_name(args)
62+
target_ociarchive = os.path.join(tmpd, "out.ociarchive")
63+
import_oci_archive(args, target_ociarchive)
64+
metadata = inspect_oci_archive(target_ociarchive)
65+
buildid = metadata['Labels']['org.opencontainers.image.version']
66+
arch = get_basearch()
67+
68+
os.makedirs(f'builds/{buildid}/{arch}/', exist_ok=True)
69+
70+
manifest_metadata = generate_manifest_json(target_ociarchive, name, buildid, arch)
71+
72+
meta_json = generate_meta_json(target_ociarchive, metadata, manifest_metadata, name)
73+
74+
archive_path = meta_json['images']['ostree']['path']
75+
76+
# Move ociarchive to build dir
77+
shutil.move(target_ociarchive, f'builds/{buildid}/{arch}/{archive_path}')
78+
79+
# Symlink build to latest
80+
if os.path.exists('builds/latest'):
81+
os.remove('builds/latest')
82+
os.symlink(f'{buildid}', 'builds/latest', target_is_directory=True)
83+
84+
builds = Builds()
85+
update_builds_json(builds, buildid, arch)
86+
prepare_build(builds, buildid)
87+
88+
89+
def update_builds_json(builds, buildid, arch):
90+
builds.insert_build(buildid, arch)
91+
builds.bump_timestamp()
92+
builds.flush()
93+
94+
95+
def import_oci_archive(args, target):
96+
# the easy case first
97+
if not args.rechunk:
98+
subprocess.check_call(['skopeo', 'copy', args.srcimg,
99+
f"oci-archive:{target}"])
100+
return
101+
102+
if not args.srcimg.startswith("containers-storage:"):
103+
raise Exception("can only rechunk from containers storage")
104+
105+
srcimg = args.srcimg[len('containers-storage:'):]
106+
subprocess.check_call(["podman", "unshare", "rpm-ostree", "experimental",
107+
"compose", "build-chunked-oci", "--bootc",
108+
"--format-version=1", f"--from={srcimg}",
109+
f"--output=oci-archive:{target}"])
110+
111+
112+
def inspect_oci_archive(image):
113+
out = subprocess.check_output(['skopeo', 'inspect',
114+
f'oci-archive:{image}'])
115+
return json.loads(out)
116+
117+
118+
def generate_manifest_json(ociarchive, name, buildid, arch):
119+
manifest = subprocess.check_output(["skopeo", "inspect", "--raw", f"oci-archive:{ociarchive}"])
120+
121+
ostree_oci_manifest_path = f"{name}-{buildid}-ostree.{arch}-manifest.json"
122+
manifest_json_dest = f'builds/{buildid}/{arch}/{ostree_oci_manifest_path}'
123+
124+
manifest_json_sha256 = None
125+
manifest_json_size = None
126+
with open(manifest_json_dest, 'wb') as manifest_json:
127+
manifest_json.write(manifest)
128+
os.fchmod(manifest_json.fileno(), S_IREAD | S_IRGRP | S_IROTH)
129+
130+
manifest_json_sha256 = sha256sum_file(manifest_json_dest)
131+
manifest_json_size = os.path.getsize(manifest_json_dest)
132+
133+
manifest_metadata = {
134+
'path': ostree_oci_manifest_path,
135+
'sha256': manifest_json_sha256,
136+
'size': manifest_json_size,
137+
"skip-compression": True,
138+
}
139+
140+
return manifest_metadata
141+
142+
143+
def generate_meta_json(ociarchive, metadata, oci_manifest, name):
144+
archive_sha256sum = sha256sum_file(ociarchive)
145+
146+
# let raise if missing
147+
assert metadata['Labels']['containers.bootc'] == '1'
148+
149+
buildid = metadata['Labels']['org.opencontainers.image.version']
150+
arch = get_basearch()
151+
152+
meta_json = {
153+
# just put a dummy ref here; it won't actually be used on streams that
154+
# already moved over to OCI only, but I want this code to be testable
155+
# with e.g. `testing-devel`. we can nuke this once testing-devel has
156+
# switched over.
157+
'ostree-version': buildid, # proxy version label
158+
'buildid': buildid, # also version label
159+
'coreos-assembler.build-timestamp': metadata['Created'], # proxy OCI build timestamp
160+
'name': name,
161+
'ostree-commit': metadata['Labels']['ostree.commit'],
162+
'ostree-timestamp': metadata['Created'],
163+
'rpm-ostree-inputhash': metadata['Labels']['rpmostree.inputhash'],
164+
'images': {
165+
'ostree': {
166+
"path": f"{name}-{buildid}-ostree.{arch}.ociarchive",
167+
"sha256": archive_sha256sum,
168+
"skip-compression": True
169+
},
170+
'oci-manifest': oci_manifest,
171+
},
172+
'coreos-assembler.config-gitrev': metadata['Labels']['org.opencontainers.image.revision'], # proxy config label
173+
'coreos-assembler.basearch': arch,
174+
}
175+
176+
with open(f'builds/{buildid}/{arch}/meta.json', 'w') as meta_file:
177+
json.dump(meta_json, meta_file, indent=4)
178+
179+
return meta_json
180+
181+
182+
if __name__ == '__main__':
183+
main()

0 commit comments

Comments
 (0)