test: qualify immutable models and Phase 9 regression
MariaDB persistence qualification / mariadb (push) Has been cancelled
MariaDB persistence qualification / mariadb (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"observedUtc": "2026-10-25T11:37:24.319354+00:00",
|
||||
"status": "PASS",
|
||||
"sourceImageId": "sha256:4a04ca68fa40b3ec61cb61187b339a4d2895b375b962999e974d75556b5b1331",
|
||||
"package": "tesseract-ocr-eng",
|
||||
"version": "1:4.1.0-2",
|
||||
"artifact": "/usr/share/tesseract-ocr/5/tessdata/eng.traineddata",
|
||||
"bytes": 4113088,
|
||||
"sha256": "7d4322bd2a7749724879683fc3912cb542f19906c83bcc1a52132556427170b2",
|
||||
"licenseEvidence": {
|
||||
"path": "docs/redesign/phase-9/p9-sec-02/model/OCR_COPYRIGHT.txt",
|
||||
"sha256": "33fb8161898e34fcf5c6b8585d6be97642d7e8f34a922e783c6c5c970d0b09ad"
|
||||
},
|
||||
"exitCode": 0,
|
||||
"containerAbsent": true,
|
||||
"scope": "Installed Debian English OCR training data in the exact qualified local runtime image; source package copyright retained. No new OCR model download."
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: tessdata_fast
|
||||
Upstream-Contact: Ray Smith <theraysmith@users.sourceforge.net>
|
||||
Source: https://github.com/tesseract-ocr/tessdata_fast/
|
||||
|
||||
Files: *
|
||||
Copyright: 1988-1995 Hewlett Packard Company.
|
||||
2006-2022 Google Inc.
|
||||
License: Apache-2.0
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2007-2009 Jeffrey Ratcliffe <Jeffrey.Ratcliffe@gmail.com>
|
||||
2017-2019 Jeff Breidenbach <jab@debian.org>
|
||||
2017-2022 Alexander Pozdnyakov <almipo@mail.ru>
|
||||
License: Apache-2.0
|
||||
|
||||
License: Apache-2.0
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the license at
|
||||
.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
.
|
||||
On Debian systems, the complete text of the Apache 2.0 license can be
|
||||
found in the file
|
||||
`/usr/share/common-licenses/Apache-2.0`.
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Owned local qualification cleanup evidence."""
|
||||
import subprocess
|
||||
|
||||
|
||||
def container_absent(name):
|
||||
try:
|
||||
query=subprocess.run(['rtk','proxy','docker','ps','--all','--no-trunc','--filter',f'name=^/{name}$','--format','{{.ID}}'],capture_output=True,text=True,timeout=5)
|
||||
except (OSError,subprocess.TimeoutExpired) as error:
|
||||
raise RuntimeError('Cleanup daemon query unavailable or timed out.') from error
|
||||
if query.returncode != 0:
|
||||
raise RuntimeError('Cleanup daemon query failed; absence is unverified.')
|
||||
return not query.stdout.strip()
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"path": ".artifacts/p9-sec-02/small-fixtures.zip",
|
||||
"bytes": 9895179,
|
||||
"sha256": "a1aff347e5f08f82e8ea7176d71d00e7f83ea5735acbdc4e3bf02c73deacb643",
|
||||
"files": {
|
||||
"safetensors/README.md": "e6114f9ddddd7aec5e9858b9849d1b53dc42b596420888b82fbf3f8defb64bf7",
|
||||
"safetensors/reference-distilbart-config.json": "028d72a28b90a369eff3f99ceb585fbd2ababf070e8577d72c315757a49b8917",
|
||||
"safetensors/vocab.json": "06b4d46c8e752d410213d9548eb27a54db70fda0319b6271fb8d59dead5e1cab",
|
||||
"safetensors/merges.txt": "1ce1664773c50f3e0cc8842619a93edc4624525b728b188a9e0be33b7726adc5",
|
||||
"safetensors/tokenizer_config.json": "5e04eb606e3a1583530a42e36c2a6b6615c86f34fe77e44d9ddeb43ff940931f",
|
||||
"safetensors/config.json": "563b819e93f153a49d6364d5c90878eec01ef78f99aae0d0c8c7ccce49e517a8",
|
||||
"safetensors/generation_config.json": "fdf2a5aa742aa3bd7fcf78a14d9aae0c599e55610e7856a35a4e89a38fed50f7",
|
||||
"safetensors/model.safetensors": "35ca2b5d2c6a28696cadd0fd7dfe483d499497e315db28dfa5a509bf13da7a68",
|
||||
"safetensors/generate.py": "006cd1354c98e72ce9a7f1f57c5b3e3fd1cf16836dcb4508ca9f9930b4777fa5",
|
||||
"weights-only/config.json": "b4f823ea324a93c355ae2ae2447bed10fcdb769070eff435f175a6738f7d3c34",
|
||||
"weights-only/generate-bin.py": "5d1f53ea37cc199d876b27731a29dea74b4df7fed7151f67a49b848993517052",
|
||||
"weights-only/generation_config.json": "fdf2a5aa742aa3bd7fcf78a14d9aae0c599e55610e7856a35a4e89a38fed50f7",
|
||||
"weights-only/merges.txt": "1ce1664773c50f3e0cc8842619a93edc4624525b728b188a9e0be33b7726adc5",
|
||||
"weights-only/pytorch_model.bin": "95d578c5706c9256a2f299a4288c0cdcae0fd303fa27e4474c34300f51f195cd",
|
||||
"weights-only/README.md": "e6114f9ddddd7aec5e9858b9849d1b53dc42b596420888b82fbf3f8defb64bf7",
|
||||
"weights-only/tokenizer_config.json": "5e04eb606e3a1583530a42e36c2a6b6615c86f34fe77e44d9ddeb43ff940931f",
|
||||
"weights-only/vocab.json": "06b4d46c8e752d410213d9548eb27a54db70fda0319b6271fb8d59dead5e1cab"
|
||||
},
|
||||
"scope": "Local retained replay capsule only. No model weights committed, uploaded or published. Verify archive SHA and per-file committed manifest before restoring to NEW isolated test directories. Different generator/runtime bytes require a new manifest and qualification."
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from cleanup import container_absent
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[5]
|
||||
HERE=Path(__file__).resolve().parent
|
||||
@@ -37,14 +38,21 @@ with (out/'process.log').open('w',encoding='utf-8') as log:
|
||||
try:code=subprocess.run(['rtk','proxy',*args],cwd=ROOT,stdout=log,stderr=subprocess.STDOUT,timeout=180).returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
code=124
|
||||
check=subprocess.run(['rtk','proxy','docker','inspect','--format','{{index .Config.Labels "jobtracker.qualification"}}',name],capture_output=True,text=True)
|
||||
if check.returncode==0 and check.stdout.strip()=='p9-sec02':subprocess.run(['rtk','proxy','docker','kill',name],capture_output=True)
|
||||
try:
|
||||
check=subprocess.run(['rtk','proxy','docker','inspect','--format','{{index .Config.Labels "jobtracker.qualification"}}',name],capture_output=True,text=True,timeout=5)
|
||||
if check.returncode==0 and check.stdout.strip()=='p9-sec02':subprocess.run(['rtk','proxy','docker','kill',name],capture_output=True,timeout=10)
|
||||
except (OSError,subprocess.TimeoutExpired):pass
|
||||
deadline=time.monotonic()+5
|
||||
cleanup_error=None
|
||||
while True:
|
||||
absent=subprocess.run(['rtk','proxy','docker','inspect',name],capture_output=True).returncode!=0
|
||||
try:absent=container_absent(name)
|
||||
except RuntimeError as error:
|
||||
absent=None;cleanup_error=str(error);break
|
||||
if absent or time.monotonic()>=deadline:break
|
||||
time.sleep(.1)
|
||||
record={'lane':lane,'attempt':attempt,'startedUtc':started,'finishedUtc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'exitCode':code,'command':args,'localImageId':image,'runtimeImageId':runtime['runtimeImageId'],'containerAbsent':absent,'network':'none','readOnly':True,'user':'65534:65534','memoryLimitBytes':2147483648,'cpuLimit':2,'pidsLimit':128,'wallClockDeadlineSeconds':180,'scope':'Actual locally built source image plus exact test-only extras; no app source mount, production cache, external network, registry push or deployment.'}
|
||||
record['cleanupStatus']='ABSENT' if absent else 'UNKNOWN' if absent is None else 'PRESENT'
|
||||
record['cleanupError']=cleanup_error
|
||||
(out/'process.json').write_text(json.dumps(record,indent=2)+'\n')
|
||||
print(json.dumps(record))
|
||||
print((out/'process.log').read_text(encoding='utf-8',errors='replace')[-2500:].encode('ascii','replace').decode())
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import subprocess
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from cleanup import container_absent
|
||||
|
||||
|
||||
class PositiveCleanupEvidence(unittest.TestCase):
|
||||
def query(self, absent, available):
|
||||
def run(args, **options):
|
||||
if not available: return subprocess.CompletedProcess(args,1,'','daemon unavailable')
|
||||
if 'inspect' in args: return subprocess.CompletedProcess(args,1 if absent else 0,'','no such object' if absent else '')
|
||||
return subprocess.CompletedProcess(args,0,'' if absent else '0123456789abcdef\n','')
|
||||
return run
|
||||
|
||||
def test_successful_daemon_query_proves_absence(self):
|
||||
with patch('cleanup.subprocess.run',side_effect=self.query(True,True)):
|
||||
self.assertTrue(container_absent('jt-sec02-owned'))
|
||||
|
||||
def test_present_container_is_not_removed(self):
|
||||
with patch('cleanup.subprocess.run',side_effect=self.query(False,True)):
|
||||
self.assertFalse(container_absent('jt-sec02-owned'))
|
||||
|
||||
def test_daemon_failure_cannot_certify_cleanup(self):
|
||||
with patch('cleanup.subprocess.run',side_effect=self.query(True,False)):
|
||||
with self.assertRaises(RuntimeError): container_absent('jt-sec02-owned')
|
||||
|
||||
def test_timeout_is_unknown_and_calls_are_bounded(self):
|
||||
with patch('cleanup.subprocess.run',side_effect=subprocess.TimeoutExpired('docker',5)) as query:
|
||||
with self.assertRaises(RuntimeError): container_absent('jt-sec02-owned')
|
||||
self.assertEqual(query.call_args.kwargs['timeout'],5)
|
||||
|
||||
def test_exact_owned_name_filter_is_used(self):
|
||||
with patch('cleanup.subprocess.run',side_effect=self.query(True,True)) as query:
|
||||
self.assertTrue(container_absent('jt-sec02-owned'))
|
||||
self.assertIn('name=^/jt-sec02-owned$',query.call_args.args[0])
|
||||
self.assertNotIn('inspect',query.call_args.args[0])
|
||||
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
Reference in New Issue
Block a user