fix: render per-instance container image from the verified manifest (${PIC_IMAGE})
Unit Tests / test (push) Successful in 9m54s
Unit Tests / test (push) Successful in 9m54s
Connectivity compose-templates hardcoded an unpinned image:tag (proxy even
referenced the renamed-away svc-redsocks), so the per-instance container that
actually ran pulled an unverified :latest — bypassing the cosign/digest
verification the store performs at install. Add a ${PIC_IMAGE} render variable
that resolves to the manifest's digest-pinned, verified image; the matching
pic-services templates switch to image: ${PIC_IMAGE} so the container that runs
is exactly the ref the store verified.
Verified on pic1: rendering the proxy template yields the pinned
svc-proxy@sha256 image.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -157,6 +157,11 @@ class ServiceComposer:
|
|||||||
result = result.replace('${PIC_CELL_NAME}', cell_name)
|
result = result.replace('${PIC_CELL_NAME}', cell_name)
|
||||||
result = result.replace('${PIC_SERVICE_ID}', service_id)
|
result = result.replace('${PIC_SERVICE_ID}', service_id)
|
||||||
result = result.replace('${PIC_DATA_DIR}', str(Path(self.data_dir).resolve()))
|
result = result.replace('${PIC_DATA_DIR}', str(Path(self.data_dir).resolve()))
|
||||||
|
# ${PIC_IMAGE} resolves to the manifest's image — the digest-pinned,
|
||||||
|
# cosign-verified reference. Templates (especially instanceable ones)
|
||||||
|
# MUST use this rather than hardcoding an image:tag, so the container
|
||||||
|
# that actually runs is the same image the store verified at install.
|
||||||
|
result = result.replace('${PIC_IMAGE}', str(manifest.get('image', '')))
|
||||||
|
|
||||||
if instance_vars:
|
if instance_vars:
|
||||||
for var in ('INSTANCE_ID', 'REDIRECT_PORT'):
|
for var in ('INSTANCE_ID', 'REDIRECT_PORT'):
|
||||||
|
|||||||
@@ -88,6 +88,23 @@ class TestRenderTemplate(unittest.TestCase):
|
|||||||
result = self.composer.render_template('myservice', manifest, template)
|
result = self.composer.render_template('myservice', manifest, template)
|
||||||
self.assertEqual(result, 'ID=myservice')
|
self.assertEqual(result, 'ID=myservice')
|
||||||
|
|
||||||
|
def test_pic_image_substituted_from_manifest(self):
|
||||||
|
# ${PIC_IMAGE} must resolve to the manifest's digest-pinned image so the
|
||||||
|
# per-instance container runs the exact ref the store verified — not an
|
||||||
|
# unpinned :latest hardcoded in the template.
|
||||||
|
digest = 'git.pic.ngo/roof/svc-proxy:latest@sha256:' + 'a' * 64
|
||||||
|
manifest = _make_manifest()
|
||||||
|
manifest['image'] = digest
|
||||||
|
template = 'image: ${PIC_IMAGE}'
|
||||||
|
result = self.composer.render_template('myservice', manifest, template)
|
||||||
|
self.assertEqual(result, f'image: {digest}')
|
||||||
|
|
||||||
|
def test_pic_image_empty_when_manifest_has_no_image(self):
|
||||||
|
manifest = _make_manifest()
|
||||||
|
result = self.composer.render_template(
|
||||||
|
'myservice', manifest, 'image: ${PIC_IMAGE}')
|
||||||
|
self.assertEqual(result, 'image: ')
|
||||||
|
|
||||||
def test_pic_secret_generated_and_substituted(self):
|
def test_pic_secret_generated_and_substituted(self):
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
composer = _composer(data_dir=tmpdir)
|
composer = _composer(data_dir=tmpdir)
|
||||||
|
|||||||
Reference in New Issue
Block a user