Skip to content

Commit 2494605

Browse files
jlebonPeaceRebel
authored andcommitted
WIP: 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 2494605

2 files changed

Lines changed: 183 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: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
def populate_build_dir(args, tmpd):
53+
name = extract_name(args)
54+
target_ociarchive = os.path.join(tmpd, "out.ociarchive")
55+
import_oci_archive(args, target_ociarchive)
56+
metadata = inspect_oci_archive(target_ociarchive)
57+
buildid = metadata['Labels']['org.opencontainers.image.version']
58+
arch = get_basearch()
59+
60+
os.makedirs(f'builds/{buildid}/{arch}/', exist_ok=True)
61+
62+
manifest_metadata = generate_manifest_json(target_ociarchive, name, buildid, arch)
63+
64+
meta_json = generate_meta_json(target_ociarchive, metadata, manifest_metadata, name)
65+
66+
archive_path = meta_json['images']['ostree']['path']
67+
68+
# Move ociarchive to build dir
69+
shutil.move(target_ociarchive, f'builds/{buildid}/{arch}/{archive_path}')
70+
71+
# Symlink build to latest
72+
if os.path.exists('builds/latest'):
73+
os.remove('builds/latest')
74+
os.symlink(f'{buildid}', 'builds/latest', target_is_directory=True)
75+
76+
builds = Builds()
77+
update_builds_json(builds, buildid, arch)
78+
builddir = builds.get_build_dir(buildid)
79+
buildmeta = builds.get_build_meta(buildid)
80+
import_ostree_commit("", builddir, buildmeta, extract_json=0)
81+
82+
# prep_build
83+
# populate image.json. Refer cmdlib ln #178-210
84+
85+
86+
def update_builds_json(builds, buildid, arch):
87+
builds.insert_build(buildid, arch)
88+
builds.bump_timestamp()
89+
builds.flush()
90+
91+
92+
def import_oci_archive(args, target):
93+
# the easy case first
94+
if not args.rechunk:
95+
subprocess.check_call(['skopeo', 'copy', args.srcimg,
96+
f"oci-archive:{target}"])
97+
return
98+
99+
if not args.srcimg.startswith("containers-storage:"):
100+
raise Exception("can only rechunk from containers storage")
101+
102+
srcimg = args.srcimg[len('containers-storage:'):]
103+
subprocess.check_call(["podman", "unshare", "rpm-ostree", "experimental",
104+
"compose", "build-chunked-oci", "--bootc",
105+
"--format-version=1", f"--from={srcimg}",
106+
f"--output=oci-archive:{target}"])
107+
108+
109+
def inspect_oci_archive(image):
110+
out = subprocess.check_output(['skopeo', 'inspect',
111+
f'oci-archive:{image}'])
112+
return json.loads(out)
113+
114+
115+
def generate_manifest_json(ociarchive, name, buildid, arch):
116+
manifest = subprocess.check_output(["skopeo", "inspect", "--raw", f"oci-archive:{ociarchive}"])
117+
118+
ostree_oci_manifest_path = f"{name}-{buildid}-ostree.{arch}-manifest.json"
119+
manifest_json_dest = f'builds/{buildid}/{arch}/{ostree_oci_manifest_path}'
120+
121+
manifest_json_sha256 = None
122+
manifest_json_size = None
123+
with open(manifest_json_dest, 'wb') as manifest_json:
124+
manifest_json.write(manifest)
125+
os.fchmod(manifest_json.fileno(), S_IREAD | S_IRGRP | S_IROTH)
126+
127+
manifest_json_sha256 = sha256sum_file(manifest_json_dest)
128+
manifest_json_size = os.path.getsize(manifest_json_dest)
129+
130+
manifest_metadata = {
131+
'path': ostree_oci_manifest_path,
132+
'sha256': manifest_json_sha256,
133+
'size': manifest_json_size,
134+
"skip-compression": True,
135+
}
136+
137+
return manifest_metadata
138+
139+
140+
def generate_meta_json(ociarchive, metadata, oci_manifest, name):
141+
archive_sha256sum = sha256sum_file(ociarchive)
142+
143+
# let raise if missing
144+
assert metadata['Labels']['containers.bootc'] == '1'
145+
146+
buildid = metadata['Labels']['org.opencontainers.image.version']
147+
arch = get_basearch()
148+
stream = metadata['Labels']['fedora-coreos.stream']
149+
150+
meta_json = {
151+
# just put a dummy ref here; it won't actually be used on streams that
152+
# already moved over to OCI only, but I want this code to be testable
153+
# with e.g. `testing-devel`. we can nuke this once testing-devel has
154+
# switched over.
155+
'ref': f"fedora/{arch}/coreos/{stream}",
156+
'ostree-version': buildid, # proxy version label
157+
'buildid': buildid, # also version label
158+
'coreos-assembler.build-timestamp': metadata['Created'], # proxy OCI build timestamp
159+
'name': name,
160+
'ostree-commit': metadata['Labels']['ostree.commit'],
161+
'ostree-timestamp': metadata['Created'],
162+
'rpm-ostree-inputhash': metadata['Labels']['rpmostree.inputhash'],
163+
'images': {
164+
'ostree': {
165+
"path": f"fedora-coreos-{buildid}-ostree.{arch}.ociarchive",
166+
"sha256": archive_sha256sum,
167+
"skip-compression": True
168+
},
169+
'oci-manifest': oci_manifest,
170+
},
171+
'coreos-assembler.config-gitrev': metadata['Labels']['org.opencontainers.image.revision'], # proxy config label
172+
'coreos-assembler.basearch': arch,
173+
}
174+
175+
with open(f'builds/{buildid}/{arch}/meta.json', 'w') as meta_file:
176+
json.dump(meta_json, meta_file, indent=4)
177+
178+
return meta_json
179+
180+
181+
if __name__ == '__main__':
182+
main()

0 commit comments

Comments
 (0)