diff --git a/README.md b/README.md index 612393d5a..22aa1c43e 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,26 @@ GitHub CodeQL independently scans the Actions and Python sources. A Server release pins the Catalog only after those gates pass; none of these checks is a production-readiness claim. +## Resource Scheduler 0.8.19 + +Catalog version `5` selects the published Resource Scheduler `v0.8.19`. +It changes only the image and visible version; questions, credentials, +commands, scheduling labels, and port-80 liveness checks retain version `4`'s +contract. Versions `1` through `4` remain unchanged for installed stacks. +The official release's all-severity runtime scan has 0 High/Critical, +0 vendor-fixed findings, and 0 secrets; 4 Low and 47 Medium vendor-unfixed +findings remain `under_investigation`, not resolved. The published manifest, +source commit, checksum-verified CycloneDX SBOM, and verified GitHub build +provenance are recorded in `catalog-images.json` and the scheduler entry in +`catalog-provenance.json`. + +The component passed reproducible builds, reachable Go vulnerability analysis, +and an isolated packaged-health fixture (non-root port 80, readiness 200, +dependency failure 503, liveness 200, zero restarts). This publication and +Catalog evidence does not qualify a managed-system upgrade or rollback. +Builder-only kernel-header applicability is recorded separately from its raw +findings and does not assess any deployed host kernel. + ## Version retention contract A numeric template version becomes immutable after a Server release or a live diff --git a/SUPPORTED.md b/SUPPORTED.md index 592f38a03..55d3d5bd7 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -21,7 +21,7 @@ or out-of-scope templates from being presented as deployable software. | PastureStack Windows Network Services | Windows infrastructure Metadata and DNS stack | v0.3.0 | Two public PastureStack GHCR Windows images with explicit version tags | Source tests, cross-compilation, PE, license, anonymous distribution, and vulnerability gates passed; Windows-host runtime pending | | PastureStack Windows Container Networking | Windows NAT and transparent network-driver definition | v0.1.0 | Reviewed selector-only placeholder image with an explicit version tag | Catalog rendering and compatibility-boundary review passed; Windows-host network-driver lifecycle pending | | PastureStack Layer 2 Flat Network | Optional infrastructure network driver | v0.3.0 | Reviewed network image with a semantic version tag; bridge setup is opt-in | Packaged Flat CNI ADD/DEL and isolated physical-bridge setup passed | -| Resource Scheduler | Infrastructure scheduling agent | v0.8.17 | Public PastureStack GHCR image with an explicit version tag | Production timeout fix, managed allocation, idempotent retry, and restart stability passed | +| Resource Scheduler | Infrastructure scheduling agent | v0.8.19 candidate | Published component v0.8.19, manifest digest and verified SBOM provenance recorded | Formal component release, reproducible builds, all-severity runtime gate, and isolated packaged-health CI passed; these checks do not qualify managed-system upgrade or rollback | | PastureStack Route 53 DNS Sync | Infrastructure external-DNS agent | v0.8.0 | Public PastureStack GHCR image with an explicit version tag | Route 53 create, update, restart, removal, health, secret, and vulnerability gates passed | | PastureStack Amazon EBS Storage | Infrastructure block-storage driver | v0.10.0 | Public PastureStack GHCR image with an explicit version tag | Existing-volume contract, safe opt-in provisioning boundary, anonymous distribution, and HIGH/CRITICAL scan passed; live AWS lifecycle pending | | PastureStack Amazon EFS Storage | Infrastructure shared-storage driver | v0.10.0 | Public PastureStack GHCR image with an explicit version tag | Existing-filesystem contract, controlled mount-target provisioning boundary, anonymous distribution, and HIGH/CRITICAL scan passed; live AWS lifecycle pending | @@ -136,13 +136,19 @@ credentials, or secret input. ## Resource Scheduler evidence -- Image: `ghcr.io/pasturestack/resource-scheduler:v0.8.15` -- Source: [`PastureStack/resource-scheduler@0423c1d2af3859cc1145fe2c5ed2407b312a5bb9`](https://github.com/PastureStack/resource-scheduler/tree/0423c1d2af3859cc1145fe2c5ed2407b312a5bb9) -- Release: [`v0.8.15`](https://github.com/PastureStack/resource-scheduler/releases/tag/v0.8.15) +- Image: `ghcr.io/pasturestack/resource-scheduler:v0.8.19`, Catalog version `5` +- Source: [`PastureStack/resource-scheduler@d1720ddb65a285ed615f1461ccb44e50c2ad67a7`](https://github.com/PastureStack/resource-scheduler/tree/d1720ddb65a285ed615f1461ccb44e50c2ad67a7) +- Release: [`v0.8.19`](https://github.com/PastureStack/resource-scheduler/releases/tag/v0.8.19) - License: Apache-2.0 for the project; Ubuntu and bundled dependencies retain their upstream licenses and notices -- Reviewed: 2026-07-24 -- Vulnerability gate: 0 HIGH and 0 CRITICAL findings with Trivy 0.72.0 -- Runtime gate: the production service remained healthy for more than four minutes with `start_count=1`; the replaced build had reached `start_count=110` +- Reviewed: 2026-10-20 +- Vulnerability gate: Trivy 0.74.0, 0 HIGH/CRITICAL, 0 vendor-fixed findings, 0 secrets; 4 LOW and 47 MEDIUM findings remain vendor-unfixed and `under_investigation` +- Provenance: official release SHA256SUMS, SBOM and runtime image GitHub attestations verified against the exact release commit, tag, and release workflow; manifest and SBOM hashes are recorded in `catalog-images.json` +- Packaged-health gate: UID/GID `10001:10001`, port 80 capability, readiness 200, dependency failure 503, liveness 200, and zero container restarts using run-owned CI fixtures without volumes + +This publication evidence does not qualify a managed-system upgrade or rollback. +Historical `v0.8.15` production evidence remains specific to that version: it +stayed healthy for more than four minutes with `start_count=1`; the replaced +build had reached `start_count=110`. The functional runtime passed race-enabled tests, source and license gates, reproducible binary and image builds, non-root execution, public version-tag diff --git a/catalog-images.json b/catalog-images.json index 909baae1a..ae2761f0d 100644 --- a/catalog-images.json +++ b/catalog-images.json @@ -619,6 +619,47 @@ "evidence": "https://github.com/PastureStack/resource-scheduler/releases/tag/v0.8.18" } }, + { + "reference": "ghcr.io/pasturestack/resource-scheduler:v0.8.19", + "manifestDigest": "sha256:46dfecc72fc1fceab5c66f881f46449874b6a084a208f59c6ef1184a0d15c3f9", + "sourceRepository": "https://github.com/PastureStack/resource-scheduler", + "sourceCommit": "d1720ddb65a285ed615f1461ccb44e50c2ad67a7", + "sourcePath": "package/Dockerfile", + "registryPage": "https://github.com/orgs/PastureStack/packages/container/package/resource-scheduler", + "licenseBoundary": "Apache-2.0 source and image; bundled Ubuntu and other packages retain their upstream licenses and notices", + "reviewedAt": "2026-10-20", + "platforms": [ + "linux/amd64" + ], + "sbom": { + "format": "CycloneDX", + "url": "https://github.com/PastureStack/resource-scheduler/releases/download/v0.8.19/resource-scheduler-0.8.19-image.cdx.json", + "sha256": "1ffbad2e58a5eaa5b7d72c83f8d8586f9f0eda337aacc41eb8d05a106951dc6e", + "provenance": { + "verified": true, + "predicateType": "https://slsa.dev/provenance/v1", + "signerWorkflow": "PastureStack/resource-scheduler/.github/workflows/release.yml", + "sourceRef": "refs/tags/v0.8.19", + "sourceCommit": "d1720ddb65a285ed615f1461ccb44e50c2ad67a7" + } + }, + "vulnerabilityScan": { + "scanner": "Trivy 0.74.0", + "reportCreatedAt": "2026-10-20T02:44:10.580805315Z", + "scope": "published runtime image", + "unknown": 0, + "low": 4, + "medium": 47, + "high": 0, + "critical": 0, + "secrets": 0, + "fixedAvailable": 0, + "vendorUnfixedLowMedium": 51, + "actionable": 0, + "disposition": "under_investigation", + "evidence": "https://github.com/PastureStack/resource-scheduler/releases/tag/v0.8.19" + } + }, { "reference": "ghcr.io/pasturestack/nfs-storage-driver:v0.9.13", "sourceRepository": "https://github.com/PastureStack/storage-plugins", diff --git a/catalog-provenance.json b/catalog-provenance.json index 852aeb3dd..10bfa3e0b 100644 --- a/catalog-provenance.json +++ b/catalog-provenance.json @@ -59,7 +59,27 @@ }, "resource-scheduler": { "sourcePath": "infra-templates/scheduler", - "originStatus": "supported" + "originStatus": "supported", + "maintainedRelease": { + "reference": "ghcr.io/pasturestack/resource-scheduler:v0.8.19", + "releaseTag": "v0.8.19", + "releaseUrl": "https://github.com/PastureStack/resource-scheduler/releases/tag/v0.8.19", + "sourceRepository": "https://github.com/PastureStack/resource-scheduler", + "sourceCommit": "d1720ddb65a285ed615f1461ccb44e50c2ad67a7", + "manifestDigest": "sha256:46dfecc72fc1fceab5c66f881f46449874b6a084a208f59c6ef1184a0d15c3f9", + "sbom": { + "format": "CycloneDX", + "url": "https://github.com/PastureStack/resource-scheduler/releases/download/v0.8.19/resource-scheduler-0.8.19-image.cdx.json", + "sha256": "1ffbad2e58a5eaa5b7d72c83f8d8586f9f0eda337aacc41eb8d05a106951dc6e", + "provenance": { + "verified": true, + "predicateType": "https://slsa.dev/provenance/v1", + "signerWorkflow": "PastureStack/resource-scheduler/.github/workflows/release.yml", + "sourceRef": "refs/tags/v0.8.19", + "sourceCommit": "d1720ddb65a285ed615f1461ccb44e50c2ad67a7" + } + } + } }, "route53-dns-sync": { "sourcePath": "infra-templates/route53", diff --git a/infra-templates/resource-scheduler/5/README.md b/infra-templates/resource-scheduler/5/README.md new file mode 100644 index 000000000..76f1b5dd6 --- /dev/null +++ b/infra-templates/resource-scheduler/5/README.md @@ -0,0 +1,24 @@ + + +# Resource Scheduler v0.8.19 + +This template deploys the functional PastureStack resource scheduler as the +control plane's scheduling agent. It consumes the established control-plane +event and Metadata contracts while using the native PastureStack executable, +debug option, public image, and semantic version tag. + +The runtime executes as UID and GID `10001:10001`. Only the scheduler binary +has permission to bind the compatibility health-check port. + +This version reconnects the control-plane event subscriber after a clean +disconnect or an error and restarts the Metadata watcher after an error or a +recovered panic. + +`/healthcheck` reports process liveness. `/readiness` reports whether the +control-plane and Metadata dependencies are currently available. A planned +control-plane restart can therefore make the service temporarily unready +without replacing the scheduler container. + +This dependency-only security update uses the official Go and Ubuntu fixes. +Scheduling, questions, credentials, and health-check contracts are unchanged. +Packaged CI health evidence does not qualify a managed-system upgrade or rollback. diff --git a/infra-templates/resource-scheduler/5/README.zh-TW.md b/infra-templates/resource-scheduler/5/README.zh-TW.md new file mode 100644 index 000000000..cb1bfeb82 --- /dev/null +++ b/infra-templates/resource-scheduler/5/README.zh-TW.md @@ -0,0 +1,20 @@ + + +# 資源排程器 v0.8.19 + +此範本會把可正常運作的 PastureStack 資源排程器部署成控制平面的 +排程代理程式。排程器使用既有的控制平面事件及中繼資料契約,同時採用 +原生 PastureStack 執行檔、除錯選項、公開映像與語意化版本標籤。 + +執行環境使用 UID/GID `10001:10001`。只有排程器執行檔具備綁定 +相容健康檢查連接埠的權限。 + +此版本會在控制平面事件訂閱正常中斷或發生錯誤後重新連線,並在中繼 +資料監看發生錯誤或已攔截的異常後重新啟動監看程序。 + +`/healthcheck` 只回報處理程序是否存活;`/readiness` 則回報控制平面與 +中繼資料相依服務目前是否可用。因此,控制平面進行計畫性重新啟動時, +排程器可以暫時呈現尚未就緒,而不會因此被替換容器。 + +此次僅採用 Go 與 Ubuntu 官方相依套件安全修補,排程、問題欄位、憑證及 +健康檢查契約均維持不變。成品 CI 健康驗證不代表已完成受管系統升級或回復驗收。 diff --git a/infra-templates/resource-scheduler/5/docker-compose.yml b/infra-templates/resource-scheduler/5/docker-compose.yml new file mode 100644 index 000000000..4881f7567 --- /dev/null +++ b/infra-templates/resource-scheduler/5/docker-compose.yml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +version: '2' +services: + resource-scheduler: + image: ghcr.io/pasturestack/resource-scheduler:v0.8.19 + command: resource-scheduler --metadata-address 169.254.169.250 + environment: + PASTURESTACK_DEBUG: '${PASTURESTACK_DEBUG}' + labels: + io.rancher.container.create_agent: 'true' + io.rancher.container.agent_service.scheduling: 'true' + logging: + driver: json-file + options: + max-size: 25m + max-file: '2' diff --git a/infra-templates/resource-scheduler/5/rancher-compose.yml b/infra-templates/resource-scheduler/5/rancher-compose.yml new file mode 100644 index 000000000..83509b156 --- /dev/null +++ b/infra-templates/resource-scheduler/5/rancher-compose.yml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: MIT +.catalog: + name: Resource Scheduler + description: Schedule native workloads from control-plane events and current Metadata state. + version: v0.8.19 + minimum_rancher_version: v1.6.26-rc1 + labels: + io.pasturestack.catalog.question.pasturestack_debug.label.zh-tw: '啟用除錯日誌' + io.pasturestack.catalog.question.pasturestack_debug.description.zh-tw: '記錄較詳細的排程器診斷資訊。' + io.pasturestack.catalog.question.healthcheck_interval.label.zh-tw: '健康檢查間隔' + io.pasturestack.catalog.question.healthcheck_interval.description.zh-tw: '兩次健康檢查之間的毫秒數。' + io.pasturestack.catalog.question.healthcheck_init_timeout.label.zh-tw: '初始化逾時' + io.pasturestack.catalog.question.healthcheck_init_timeout.description.zh-tw: '等待排程器初次就緒的毫秒數上限。' + io.pasturestack.catalog.question.healthcheck_reinit_timeout.label.zh-tw: '重新初始化逾時' + io.pasturestack.catalog.question.healthcheck_reinit_timeout.description.zh-tw: '等待排程器重新初始化的毫秒數上限。' + io.pasturestack.catalog.question.healthcheck_response_timeout.label.zh-tw: '回應逾時' + io.pasturestack.catalog.question.healthcheck_response_timeout.description.zh-tw: '等待單次健康檢查回應的毫秒數上限。' + io.pasturestack.catalog.question.healthcheck_unhealthy_threshold.label.zh-tw: '判定為不健康的失敗次數' + io.pasturestack.catalog.question.healthcheck_unhealthy_threshold.description.zh-tw: '連續失敗達到此次數時,將服務標示為不健康。' + io.pasturestack.catalog.question.healthcheck_healthy_threshold.label.zh-tw: '判定為健康的成功次數' + io.pasturestack.catalog.question.healthcheck_healthy_threshold.description.zh-tw: '連續成功達到此次數時,將服務標示為健康。' + questions: + - variable: PASTURESTACK_DEBUG + label: Enable debug logging + description: Enable verbose scheduler diagnostics. + type: boolean + default: false + required: true + - variable: HEALTHCHECK_INTERVAL + label: Health-check interval + description: Milliseconds between health checks. + type: int + default: 2000 + required: true + - variable: HEALTHCHECK_INIT_TIMEOUT + label: Initialization timeout + description: Milliseconds allowed for initial scheduler readiness. + type: int + default: 10000 + required: true + - variable: HEALTHCHECK_REINIT_TIMEOUT + label: Reinitialization timeout + description: Milliseconds allowed for scheduler reinitialization. + type: int + default: 10000 + required: true + - variable: HEALTHCHECK_RESPONSE_TIMEOUT + label: Response timeout + description: Milliseconds allowed for an individual health-check response. + type: int + default: 30000 + required: true + - variable: HEALTHCHECK_UNHEALTHY_THRESHOLD + label: Unhealthy threshold + description: Consecutive failed checks required to mark the service unhealthy. + type: int + default: 3 + required: true + - variable: HEALTHCHECK_HEALTHY_THRESHOLD + label: Healthy threshold + description: Consecutive successful checks required to mark the service healthy. + type: int + default: 2 + required: true + +resource-scheduler: + health_check: + request_line: GET /healthcheck HTTP/1.0 + port: 80 + interval: ${HEALTHCHECK_INTERVAL} + initializing_timeout: ${HEALTHCHECK_INIT_TIMEOUT} + reinitializing_timeout: ${HEALTHCHECK_REINIT_TIMEOUT} + response_timeout: ${HEALTHCHECK_RESPONSE_TIMEOUT} + unhealthy_threshold: ${HEALTHCHECK_UNHEALTHY_THRESHOLD} + healthy_threshold: ${HEALTHCHECK_HEALTHY_THRESHOLD} diff --git a/infra-templates/resource-scheduler/config.yml b/infra-templates/resource-scheduler/config.yml index cdf64750f..6985b36bb 100644 --- a/infra-templates/resource-scheduler/config.yml +++ b/infra-templates/resource-scheduler/config.yml @@ -1,6 +1,6 @@ # SPDX-License-Identifier: MIT name: Resource Scheduler -version: v0.8.18 +version: v0.8.19 category: System description: Schedule native workloads from control-plane events and current Metadata state. projectURL: https://github.com/PastureStack/resource-scheduler diff --git a/integration/core/test_catalog.py b/integration/core/test_catalog.py index 36cb0930a..6bb824103 100644 --- a/integration/core/test_catalog.py +++ b/integration/core/test_catalog.py @@ -266,10 +266,10 @@ def test_catalog_list(): assert by_folder[('infra', 'resource-scheduler')][ 'name'] == 'Resource Scheduler' assert by_folder[('infra', 'resource-scheduler')][ - 'defaultVersion'] == 'v0.8.18' + 'defaultVersion'] == 'v0.8.19' assert by_folder[('infra', 'resource-scheduler')][ 'links']['defaultVersion'].endswith( - ':4') + ':5') assert by_folder[('infra', 'route53-dns-sync')][ 'name'] == 'Route 53 DNS Sync' assert by_folder[('infra', 'route53-dns-sync')][ @@ -920,7 +920,7 @@ def test_catalog_compose_shapes_are_runtime_compatible(): scheduler_files = scheduler_version['files'] scheduler_docker = scheduler_files['docker-compose.yml'] scheduler_platform = scheduler_files['rancher-compose.yml'] - scheduler_image = 'ghcr.io/pasturestack/resource-scheduler:v0.8.18' + scheduler_image = 'ghcr.io/pasturestack/resource-scheduler:v0.8.19' assert scheduler_docker.count( 'image: {}'.format(scheduler_image)) == 1 assert '\n resource-scheduler:\n' in scheduler_docker @@ -1201,3 +1201,85 @@ def test_network_services_upgrade_versions_remain_available( assert ' version: ' + label + '\n' in files['rancher-compose.yml'] assert 'README.md' in files assert 'README.zh-TW.md' in files + + +def test_scheduler19_template_preserves_version4_contract(): + template = 'infra-templates/resource-scheduler' + old_image = 'ghcr.io/pasturestack/resource-scheduler:v0.8.18' + image = 'ghcr.io/pasturestack/resource-scheduler:v0.8.19' + with open(_file(template + '/4/docker-compose.yml'), + encoding='utf-8') as source: + old_compose = source.read() + with open(_file(template + '/5/docker-compose.yml'), + encoding='utf-8') as source: + compose = source.read() + assert old_compose.count('image: ' + old_image) == 1 + assert compose == old_compose.replace(old_image, image) + assert '@sha256:' not in compose + with open(_file(template + '/4/rancher-compose.yml'), + encoding='utf-8') as source: + old_catalog = source.read() + with open(_file(template + '/5/rancher-compose.yml'), + encoding='utf-8') as source: + catalog = source.read() + assert catalog == old_catalog.replace( + ' version: v0.8.18\n', ' version: v0.8.19\n') + + with open(_file('catalog-images.json'), encoding='utf-8') as source: + images = { + item['reference']: item for item in json.load(source)['images'] + } + assert images[old_image]['sourceCommit'] == ( + '033f0f4fb9b13b7524216accd5d9bab804a3b69b') + release = images[image] + assert release['sourceCommit'] == ( + 'd1720ddb65a285ed615f1461ccb44e50c2ad67a7') + assert release['platforms'] == ['linux/amd64'] + assert release['manifestDigest'] == ( + 'sha256:46dfecc72fc1fceab5c66f881f464498' + '74b6a084a208f59c6ef1184a0d15c3f9') + scan = release['vulnerabilityScan'] + assert scan['scope'] == 'published runtime image' + assert scan['high'] == scan['critical'] == scan['secrets'] == 0 + assert scan['fixedAvailable'] == scan['actionable'] == 0 + assert scan['low'] + scan['medium'] == scan['vendorUnfixedLowMedium'] + assert scan['disposition'] == 'under_investigation' + sbom = release['sbom'] + assert sbom['format'] == 'CycloneDX' + assert sbom['sha256'] == ( + '1ffbad2e58a5eaa5b7d72c83f8d8586f9' + 'f0eda337aacc41eb8d05a106951dc6e') + assert sbom['url'].endswith( + '/v0.8.19/resource-scheduler-0.8.19-image.cdx.json') + provenance = sbom['provenance'] + assert provenance['verified'] is True + assert provenance['sourceCommit'] == release['sourceCommit'] + assert provenance['sourceRef'] == 'refs/tags/v0.8.19' + assert provenance['predicateType'] == 'https://slsa.dev/provenance/v1' + assert provenance['signerWorkflow'] == ( + 'PastureStack/resource-scheduler/.github/workflows/release.yml') + with open(_file('catalog-provenance.json'), encoding='utf-8') as source: + maintained = json.load(source)['entries'][ + 'resource-scheduler']['maintainedRelease'] + assert maintained['reference'] == image + assert maintained['sourceCommit'] == release['sourceCommit'] + assert maintained['manifestDigest'] == release['manifestDigest'] + assert maintained['sbom'] == sbom + + +@pytest.mark.parametrize('number,image_tag', [ + ('4', 'v0.8.18'), + ('5', 'v0.8.19'), +]) +def test_scheduler_upgrade_versions_remain_available(number, image_tag): + version = _get_json(_catalog_url( + '/v1-catalog/templateversions/{}:infra*resource-scheduler:{}'.format( + _catalog_name(), number))) + files = version['files'] + assert ('image: ghcr.io/pasturestack/resource-scheduler:' + image_tag + in files['docker-compose.yml']) + assert ' version: ' + image_tag + '\n' in files['rancher-compose.yml'] + assert 'request_line: GET /healthcheck HTTP/1.0' in ( + files['rancher-compose.yml']) + assert 'README.md' in files + assert 'README.zh-TW.md' in files diff --git a/scripts/audit_deployable_images.py b/scripts/audit_deployable_images.py index a2413d980..4cea6ff20 100644 --- a/scripts/audit_deployable_images.py +++ b/scripts/audit_deployable_images.py @@ -60,7 +60,7 @@ "network-services": ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), "nfs-storage": ("1", "2"), "per-host-subnet-network": ("2", "3", "4"), - "resource-scheduler": ("1", "2", "3", "4"), + "resource-scheduler": ("1", "2", "3", "4", "5"), "secret-volume-driver": ("1", "2"), "vxlan-overlay-network": ("2", "3", "4", "5"), } diff --git a/scripts/test b/scripts/test index 6850d1dda..f7bff4b5b 100755 --- a/scripts/test +++ b/scripts/test @@ -134,6 +134,7 @@ for version_id in \ resource-scheduler:2 \ resource-scheduler:3 \ resource-scheduler:4 \ + resource-scheduler:5 \ secret-volume-driver:1 \ secret-volume-driver:2 \ layer-2-flat-network:4 \