Guard cache path normalization so rootDir is joined only for truly cache-relative
records. This prevents malformed paths like root/root/... when dstRecord already
contains rootDir, avoiding stat failures and unnecessary cache churn.
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
Extract the blob-to-blobstore promotion logic into a separate helper method
(promoteBlobCandidate) to reduce the main function's cyclomatic complexity from
34 to 28, bringing it within the gocyclo threshold of 30.
Also move blobCandidate and repoBlobRef type definitions to package level to
support both the main function and helper.
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
- Use a dedicated migration marker (_blobstore/.migrated) instead of
the heuristic blob-count sentinel in upgradeToGlobalBlobstore; this
correctly skips the upgrade scan on fresh installs where the blobstore
is empty and has never had blobs.
- Remove the stale gc.CleanRepo ShouldNotBeNil assertion in local_test.go
that had no state change between calls and was incorrect once CleanRepo
became idempotent for missing blobs.
- Accept HTTP 409 Conflict (bucket already exists) as a success case in
the three S3 bucket-creation panics in controller_test.go, preventing
test flakiness when the S3 mock retains bucket state across Convey blocks.
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
* feat(storage): add a common blobstore to store all blobs (#3906)
Currently, zot uses one of the existing repos as the master copy for a blob to
achieve dedupe, which complicates dedupe tracking logic. Furthermore, we
have a global storage lock which is becoming a bottleneck. In order to
move to a per-repo lock, we first need to simplify this logic.
Now use a single hidden global repo (_blobstore/) as a blob store instead.
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
* fix(storage): address blobstore cache correctness issues
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
---------
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
* refactor(test/blackbox): extract shared push/pull helpers
Move duplicated push/pull/regclient/oras/helm helper functions out of
fips140.bats, pushpull.bats and helpers_upgrade.bash into a single
helpers_pushpull.bash, then have all three suites load from there.
The helpers now share verify_prerequisites, get_zot_port, common assertion
helpers (catalog/tag presence, OCI index ref name) and the regclient
pagination listing. helpers_upgrade.bash keeps only the test_release_*
and test_new_* wrappers that compose those helpers.
Net effect: ~1000 lines of duplicated test scaffolding removed across
the four files; behavior of the existing test cases is preserved.
Refs: #3727
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* refactor(test/blackbox): share pushpull lifecycle and dedupe authn helpers
Build on the shared helpers_pushpull.bash to remove more duplicated
blackbox scaffolding:
- Add pushpull_setup_file/pushpull_teardown/pushpull_teardown_file keyed
on PUSHPULL_FIPS_MODE so pushpull.bats and fips140.bats only set the
flag and delegate lifecycle, instead of each carrying a near-identical
setup_file/teardown.
- Add helpers_pushpull_authn.bash (loaded by pushpull_authn.bats and
fips140_authn.bats) for shared htpasswd setup, FIPS vs non-FIPS config
and teardown, and the regclient/OCI/ML test helpers; both authn suites
collapse to one-line @test bodies keyed on PUSHPULL_AUTHN_FIPS_MODE.
- Make each authn helper self-sufficient by performing its own regctl
login, removing the hidden dependency on the first test having logged
in.
- Split the implicit manifest delete out of helper_pull_image_index_and_delete
into a standalone helper_delete_manifest, surfaced as its own
"delete image index" @test in the pushpull, fips140 and upgrade suites,
so the delete is explicit and no longer an order-fragile side effect of
a pull.
Refs: #3727
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): harden flaky oras pull and sync signature tests
Set org.opencontainers.image.title on oras artifact push and verify pull
both via oras pull and manifest/blob fetch. Add retry_until_success and
use it for periodic notation/cosign signature sync checks on slow CI.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): address Copilot review feedback in push/pull helpers
Use curl --fail for manifest deletes so HTTP errors fail the test. Remove
the duplicate regctl --format flag in helper_push_manifest_with_regclient.
Harden helper_authn_ml_artifacts with run and status checks, using a
binary-safe shell redirect for the ONNX artifact round-trip.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): isolate pushpull helper temp files and prerequisites
Write oras artifact and docker build files under BATS_TEST_TMPDIR
instead of the test working directory, and check git/docker in
verify_prerequisites for clearer failures when running bats directly.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test): use verify_prerequisites exit status in bats setup
Replace `$(verify_prerequisites)` with a direct call across blackbox
and scale-out suites.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): unify blackbox log path to zot/zot-log.json
Drop the FIPS vs non-FIPS split between zot-log.json and zot.log in
pushpull, authn, and upgrade helpers.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* refactor(test/blackbox): loop over tools in verify_prerequisites
Replace repeated command -v checks with a single loop over curl, jq,
git, and docker.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* refactor(test/blackbox): fix exit code for retry_until_success
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* refactor(test/blackbox): inline upgrade tests and drop helpers_upgrade
Call helper_* directly from upgrade.bats and upgrade_minimal.bats,
load helpers_pushpull in those suites, and move teardown there. Remove
helpers_upgrade.bash now that the release/new wrappers are gone.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* refactor(test/blackbox): require explicit args on pushpull helpers
Drop parameter defaults from shared push/pull helpers and pass image,
repository, and pagination values explicitly at each call site.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): clarify docker push/pull negative test naming
Rename helper_push_docker_image to helper_build_docker_image_push_and_pull
and update test titles to reflect build plus expected push/pull failures
without the docker compatibility extension.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): isolate regctl config per BATS suite
regctl persists login and TLS settings on disk, so push/pull blackbox
tests could leak state into ~/.regctl/config.json across suites. Point
REGCTL_CONFIG at BATS_FILE_TMPDIR for pushpull, authn, and upgrade
suites, configure TLS once in authn setup, and drop redundant logout
and per-login TLS setup.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(test/blackbox): harden upgrade suite log dump on failure
Touch zot-log.json during upgrade setup and only cat it in teardown when
the file exists, so a missing log cannot mask the underlying test failure.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
---------
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
Co-authored-by: Akash Kumar <meakash7902@gmail.com>
* fix(storage): release global write lock during blob restore I/O
Restore-deduped-blob reads/writes no longer hold the global storage write
lock for the duration of slow S3 GETs, only for the final write. Adds a
restore-complete marker so subsequent dedupe=false startups can skip the
restore scan, and hardens DedupeTaskGenerator's completion tracking against
races between in-flight restore tasks and Reset().
Signed-off-by: shcherbak <ju.shcherbak@gmail.com>
* test(storage): reuse ThreadSafeLogBuffer instead of duplicate syncBuffer
---------
Signed-off-by: shcherbak <ju.shcherbak@gmail.com>
* fix(authz): metrics: reject users not in list even with anonymous read
Even when anonymous reads are enabled for metrics, users not in the
allowed list should not be allowed.
This change also refactors the MetricsAuthzHandler to align better
with this logic.
Signed-off-by: Vishwas Rajashekar <dev@vrajashkr.com>
* fix(authz): address review comments
Address comments to pass username when present
to AuthzFail if user is not allowed for metrics.
This changes the response to Forbidden instead of
Unauthorized.
Use isAnonymous() check instead of only checking for
empty username.
Signed-off-by: Vishwas Rajashekar <dev@vrajashkr.com>
* fix(authz): fix additional review comments
Fix a few more review comments
Signed-off-by: Vishwas Rajashekar <dev@vrajashkr.com>
---------
Signed-off-by: Vishwas Rajashekar <dev@vrajashkr.com>
This change adds validation for metrics config.
In particular, the metrics path is checked to
ensure it starts with a / and is not one of the
disallowed paths.
Signed-off-by: Vishwas Rajashekar <dev@vrajashkr.com>
GetAllDedupeReposCandidates propagated zerr.ErrCacheMiss from the cache's
GetAllBlobs verbatim. But a cache miss is the normal case for a not-yet-cached
blob — the first push of a new blob, or a cross-repo mount check during a push
— and semantically means "no dedupe/mount candidates", not a failure.
Propagating it caused canMount (used by the CheckBlob and CreateBlobUpload
handlers) to surface the error, which the route handlers log as an
"unexpected error". With remote (e.g. S3) storage a cache is always present
(dedupe:false does not disable it), so this logs an error-level line for every
fresh blob digest on every push — significant log spam during bulk pushes and
cross-repo mounts, with no functional impact (the push still succeeds via a
normal upload).
Handle ErrCacheMiss the same way as the existing nil-cache branch above:
return no candidates and no error.
Signed-off-by: Janko Thyson <janko@kaosmaps.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(metadb): add writer version to interface
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore(metadb): add writer version to db mock
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore(metadb): implement writer version for bolt, redis, and dynamodb
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* feat(metadb): add optional fast restart path that skips storage walk when binary identity matches metaDB stamp
binary identity is determined by the current release tag/commit and stored in metaDB after a successful storage parse. When fast restart is enabled, the next startup will skip the parse if the stored identity matches the current binary
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore(cli): serve: add a way to force reparse storage
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* refactor(meta): version: split to avoid global state mutation in tests
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(meta): version: include commit in writerVersion to distinguish retags
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore(config): add IsFastRestartEnabled() test
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(meta): skip writer-version stamp when storage parse is incomplete
ParseStorage returns nil even when individual repos fail to parse or are only partially parsed (a missing manifest blob), so MaybeParseStorage would stamp a partially-populated metaDB as good. On the next restart fastRestart trusts the stamp, skips the storage walk, and never recovers.
Track per-repo outcomes via parseStats and stamp only when the walk fully populated the metaDB, otherwise log and continue so the next restart reparses
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(docs): readme: remove trailing comma from JSON config
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(meta): dynamodb: use context.Background instead of context.TODO
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(meta): invalidate fast restart on storage config changes
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore(meta): dynamodb: use context.Background() instead of context.TODO()
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* docs(meta): dynamodb: add comment about nil AttributeValue handling in GetWriterVersion
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* chore: rename writer-version stamp to fast-restart stamp
also replaces the version/commit tracking to use BinaryVersion instead of WriterVersion
This should make things more clear
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(config): ensure FastRestart is on GlobalStorageConfig
This is not a per-subpath setting
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* fix(metadb): redis: tests: ensure clients are closed
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
---------
Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
* ci: migrate workflows off deprecated oracle-vm runners
Switch CI jobs from oracle-vm-* runner labels to cncf-ubuntu-* as the oracle-cm-* CNCF runner labels are deprecated.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* ci: increase resources available for ecosystem tools tests
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
---------
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* feat(metrics): add Prometheus GC metrics
Track garbage collection activity with three new metrics:
- zot_gc_runs_total (counter, label: error) — GC run count
- zot_gc_duration_seconds (summary) — GC run duration
- zot_gc_deleted_total (counter, label: type) — items deleted
by type: blob, manifest, upload
MetricServer is added to GarbageCollect and wired through
all callers (controller, verify-feature retention, tests).
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(test): add missing metrics var in GCS GC tests
TestGCSGarbageCollectImageIndex and
TestGCSGarbageCollectChainedImageIndexes were missing the
metrics variable required by NewGarbageCollect after the
MetricServer parameter was added.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(test): add defer metrics.Stop() in GC tests
Prevent goroutine/port leaks by stopping MetricsServer in
storage_test.go (3 functions) and gcs_test.go (also add
missing metrics declaration in TestGCSGarbageCollectImageManifest).
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(test): cover `CleanRepo` error path
Add test that exercises the error branch in
`CleanRepo` where `cleanRepo` fails, covering
the metrics calls and log lines flagged by Codecov.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test: Cover GC error paths for codecov
Add three tests in gc_internal_test.go to cover previously
untested error branches in `removeBlobUploads` and
`removeUnreferencedBlobs`: `ListBlobUploads` failure,
`addIndexBlobsToReferences` failure, and `PathNotFoundError`
from `GetAllBlobs`.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test(gc): cover remaining error paths
Cover `StatBlobUpload`, `digest.Validate()`,
`isBlobOlderThan`, and `CleanupRepo` error branches
in `removeBlobUploads` and `removeUnreferencedBlobs`.
`removeUnreferencedBlobs` now at 100% coverage,
`removeBlobUploads` from 78.3% to 91.3%.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test: cover `sanityChecks` label name mismatch
Try to avoid -0.09% coverage regression on `minimal.go`
by exercising the uncovered branch in `sanityChecks`
where label names have correct count but wrong values.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test(gc): exercise real GC path in metrics test
TestGCMetrics was calling metric helpers directly instead of
running actual garbage collection, so it couldn't catch wiring
regressions where `CleanRepo` stops recording metrics.
Now uploads an orphaned blob and runs `gc.CleanRepo` end-to-end,
verifying metrics appear on the Prometheus endpoint.
Suggestion from Copilot: https://github.com/project-zot/zot/pull/3863#discussion_r3129324719
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(gc): skip deletion metrics when DryRun is enabled
https://github.com/project-zot/zot/pull/3863#discussion_r3129324684
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(test): stop leaked MetricsServer goroutines in GCS tests
https://github.com/project-zot/zot/pull/3863#discussion_r3129324657
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* refactor(test): drop unnecessary zlog import alias
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(monitoring): expose metric types outside build tag
`MetricsCopy` and related types were only visible under `\!metrics`,
causing a typecheck failure when golangci-lint runs with `-tags metrics`.
Moving the type definitions to `common.go` makes them unconditionally available.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(monitoring): remove extra blank line for gci
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test(gc): cover both dry-run and real deletion metrics
And fix issue with build tag with metrics
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* Satisfy testpackage linter for gc metrics test
The `testpackage` linter allows `package gc` only in files named
`*_internal_test.go`; rename to follow that convention.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
---------
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
Remove warning:
```
WARN The linter 'gomodguard' is deprecated (since v2.12.0) due to: new major version. Replaced by gomodguard_v2.
WARN Suggested new configuration:
linters:
enable:
- gomodguard_v2
```
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
1. Cause
SBOM assets were added to the release checksum file, so you get two lines that contain the same substring, e.g. …linux-amd64.tar.gz and …linux-amd64.tar.gz.sbom.json.
The old install.sh logic used something like grep "${BASENAME}", so both lines matched. That breaks want (wrong or ambiguous hash) and shows up as checksum verification errors (including the 8df… vs fd3… style mismatch you debugged).
2. The upstream fix is in https://github.com/golangci/golangci-lint/pull/6539
The matcher was changed so the checksum line must end with the archive name — i.e. grep "${BASENAME}$" — so the .sbom.json line no longer matches.
3. In the same thread they say master on raw.githubusercontent.com is not the right branch anymore and recommend the canonical installer URL:
https://golangci-lint.run/install.sh
(see the PR description and local install / binaries.)
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
Make `zli config` and config subcommands show usage/help when invoked with
missing required args, instead of Cobra’s generic “accepts N arg(s)” errors.
Example:
- before: `zli config add` -> `Error: accepts 2 arg(s), received 0`
- after: `zli config add` prints `zli config add <config-name> <url>` usage/help1~fix(zli config): print help for missing args
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* feat(zli): add config list/show/get/set/reset and isolate deprecated syntax
Introduce first-class subcommands for listing profiles, showing a profile,
getting and setting keys, and resetting optional keys (alongside existing add/remove).
The parent command now resolves ~/.zot via zliUserConfigPath(),
documents that profile names must not clash with subcommand names,
and states that positional/--list/--reset usage is deprecated and will be removed soon.
Legacy behavior is delegated to config_cmd_deprecated.go with stderr warnings for old flags and positional get/set.
Examples and inline help point users at the new commands.
FormatNames/FormatListedVars comments reference config list/show.
Tests are split so config_cmd_test.go exercises the supported subcommands
while config_cmd_deprecated_test.go retains coverage for the deprecated
paths under renamed TestConfigCmdDeprecated* entries.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* test: stabilize retention check tests
See https://github.com/project-zot/zot/actions/runs/25361779632/job/74362802944?pr=4037
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
---------
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
Keep CLI binaries from importing pkg/api/config just for version strings by
centralizing Commit/ReleaseTag/BinaryType/GoVersion in a tiny buildinfo package.
Update ldflags targets and callers accordingly.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
feat(cli): add typed ~/.zot config layer and strict validation
Introduce pkg/cli/client/config.go with ZliConfigFile/ZliConfig and
ReadZliConfigFile, replacing the loose map[string]any load/save path in
config_cmd.go.
Parsing now rejects malformed JSON with ErrCliBadConfig and requires a
non-null configs array (ErrCliMissingConfigsField when wrapped). Each
profile must have non-empty _name and url.
Config commands delegate to typed helpers (Find, AddEntry, RemoveEntry,
GetVar/SetVar/ResetVar, FormatNames, WriteFile). Fresh or minimal files
still behave as empty via isFreshCliRead (ErrEmptyJSON or missing configs).
Tests: prefer t.Setenv("HOME", t.TempDir()) where CLI resolution uses --url
only; align CVE/client/search tests with mandatory profile URL and HOME
isolation.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
Read identity from request context after handlers run so Basic, Bearer,
OIDC, mTLS, etc. are covered; use subject "anonymous" when unset.
Redact Authorization in SessionLogger without decoding credentials.
Add session_test.go for SessionLogger and SessionAuditLogger.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
Rename getOpenIDUsername to getOpenIDIdentity and thread "identity"
through bearer OIDC, Basic-auth parsing, OAuth2Callback, and log fields.
Only fall back (and warn) to the default email claim when the configured
username claim is non-default but missing or empty.
Stop emitting Info logs when groups are absent on only UserInfo or only ID
token claims; log once at Debug when no groups remain after merging both.
Update ClaimMapping docs to mention username and groups claims; fix mTLS
extractIdentity comment typo; clarify GetAuthUserFromRequestSession doc.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
countingReader was not respecting the single responsibility principle
and the implementation was hard to understand
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* feat(auth): map OpenID groups claim
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* fix(auth): refine OIDC claim mapping logs
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* refactor(auth): collapse OIDC username fallback into nested if
Reuse the empty-username branch for the email fallback so the value is
checked once and the failure path lives next to the recovery attempt.
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* refactor(auth): consolidate OIDC claim extraction into authn.go
Move getOpenIDClaimMapping, getOpenIDUsername, and appendOpenIDGroups
out of routes.go into authn.go alongside a new extractOpenIDIdentity
helper that owns the username/groups extraction flow. This keeps the
HTTP callback in routes.go thin and groups OIDC plumbing with the rest
of the authentication code.
Also:
- Filter nil and empty entries consistently across the []any, []string,
and string branches of appendOpenIDGroups, with new test cases
covering []any{nil, ""} and []string{"admin","",...}.
- Surface a Warn log when an operator-configured username claim is
missing/empty so the fallback to email isn't silent.
- Rename openid_claim_mapping_internal_test.go to authn_internal_test.go
and drop the build tags that aren't needed for the internal tests.
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
---------
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
- document `zot schema` as the generated JSON Schema reference for
configuration files
- add a top-level configuration map to help users discover the major
config sections before reading examples
Fixes#2967
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* fix(api): set blob response Content-Type from OCI descriptor
Blob HEAD responses had no Content-Type and GET responses echoed the
request's Accept header verbatim, which produced missing or malformed
media types and left multipart/byteranges parts without a per-part
Content-Type. This breaks OCI distribution-spec conformance and
consumers like stargz-snapshotter that need a well-formed layer media
type.
Add a blobResponseMediaType helper that resolves the descriptor's
MediaType via GetBlobDescriptorFromRepo and falls back to
application/octet-stream. Use it in CheckBlob (HEAD), GetBlob full
(200), GetBlob single-range (206), and per-part in
writeMultipartRanges (206 multipart). Lookup is deferred until after
the blob is known to exist.
Cover the new behaviour with mock-based unit tests in routes_test.go
and end-to-end assertions in TestPullRange.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* perf(api): stream multipart blob ranges lazily with precomputed Content-Length
writeMultipartRanges previously opened every range reader up front
and emitted no Content-Length, so an N-range request held N
concurrent storage readers (and their fds / read buffers) per
response window and forced chunked encoding on HTTP/1.1 — neither
friendly to proxies nor to fan-out scenarios like stargz lazy pulls.
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
---------
Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
* fix(sync): apply tag filters before destination mapping
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* fix(sync): return stable pointer from getContentByUpstreamRepo
Iterate by index and return &cm.contents[i] so callers get the slice
element rather than a copy of the loop variable, matching the existing
GetContentByLocalRepo helper.
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
---------
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* fix(api): support multipart range blob pulls
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
* fix(api): tighten multipart range response
- Drop the redundant deferred closeRangeReaders; the deferred cleanup
registered when the slice is created already covers all paths.
- Stop copying the request Accept header into each multipart part's
Content-Type. Accept can be a list of media ranges (e.g.
"application/octet-stream,*/*"), which is not a valid Content-Type and
may confuse multipart parsers. RFC 9110 lets us omit it entirely.
- Set Docker-Content-Digest on the partial-content response so range
pulls expose the same header as a full GET.
- Drop the over-broad build tag on routes_internal_test.go; the parser
unit test does not need any extension build tags.
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
---------
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
POST /zot/auth/logout now returns an endSessionUrl in the JSON
response body when the session was established via an OIDC provider
whose discovery document advertises an endSessionEndpoint, so the
UI can navigate the browser to it and terminate the session at the
IdP in addition to clearing the local cookie.
- The OIDC callback records the provider name in the session after
login; the github OAuth2 path is untouched.
- end_session_endpoint is read from the zitadel/oidc RelyingParty
and validated as an absolute http(s) URL.
- post_logout_redirect_uri prefers http.externalUrl when set and
falls back to deriving the origin from the incoming request.
- No id_token_hint is sent; client_id identifies the RP, so the
ID token does not need to be persisted.
- Non-OIDC sessions (local/basic/LDAP/GitHub) retain the existing
200 OK, no body behavior.
Operators must register the URI zot sends as a valid post-logout
redirect URI on the IdP client.
Ref: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
Signed-off-by: Nikita Vakula <programmistov.programmist@gmail.com>