Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to retrieve manifest from local repo for manifest requests to proxy #21123

Closed

Conversation

raphaelzoellner
Copy link

@raphaelzoellner raphaelzoellner commented Oct 31, 2024

Comprehensive Summary of your change

Harbor Proxies will serve manifests located in the local repository if the digest of the local manifest matches the digest of the remote manifest.

Issue being fixed

Fixes #21122

Please indicate you've done the following:

  • Well Written Title and Summary of the PR
  • Label the PR as needed. "release-note/ignore-for-release, release-note/new-feature, release-note/update, release-note/enhancement, release-note/community, release-note/breaking-change, release-note/docs, release-note/infra, release-note/deprecation"
  • Accepted the DCO. Commits without the DCO will delay acceptance.
  • Made sure tests are passing and test coverage is added if needed.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed in website repository.

@raphaelzoellner raphaelzoellner requested a review from a team as a code owner October 31, 2024 15:48
@raphaelzoellner raphaelzoellner marked this pull request as draft October 31, 2024 15:51
@raphaelzoellner raphaelzoellner force-pushed the 21122-add-manifest-cache branch from c301366 to dd9af57 Compare October 31, 2024 15:57
@raphaelzoellner raphaelzoellner marked this pull request as ready for review October 31, 2024 16:01
@wy65701436 wy65701436 assigned stonezdj and unassigned OrlinVasilev and MinerYang Nov 1, 2024
Copy link

codecov bot commented Nov 4, 2024

Codecov Report

Attention: Patch coverage is 55.76923% with 23 lines in your changes missing coverage. Please review.

Project coverage is 66.10%. Comparing base (c8c11b4) to head (dd9af57).
Report is 312 commits behind head on main.

Files with missing lines Patch % Lines
src/controller/proxy/controller.go 64.44% 14 Missing and 2 partials ⚠️
src/server/middleware/repoproxy/proxy.go 0.00% 5 Missing ⚠️
src/controller/proxy/local.go 0.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main   #21123       +/-   ##
===========================================
+ Coverage   45.36%   66.10%   +20.73%     
===========================================
  Files         244     1049      +805     
  Lines       13333   114679   +101346     
  Branches     2719     2867      +148     
===========================================
+ Hits         6049    75804    +69755     
- Misses       6983    34733    +27750     
- Partials      301     4142     +3841     
Flag Coverage Δ
unittests 66.10% <55.76%> (+20.73%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/controller/proxy/local.go 35.89% <0.00%> (ø)
src/server/middleware/repoproxy/proxy.go 4.42% <0.00%> (ø)
src/controller/proxy/controller.go 28.70% <64.44%> (ø)

... and 1285 files with indirect coverage changes

… proxy

Signed-off-by: Raphael Zöllner <raphael.zoellner@regiocom.com>
@raphaelzoellner raphaelzoellner force-pushed the 21122-add-manifest-cache branch from dd9af57 to 0a326e6 Compare November 4, 2024 12:59
@@ -175,35 +213,64 @@ func (c *controller) UseLocalManifest(ctx context.Context, art lib.ArtifactInfo,
return false, nil, errors.NotFoundError(fmt.Errorf("repo %v, tag %v not found", art.Repository, art.Tag))
}

// Use digest to retrieve manifest, digest is more stable than tag, because tag could be updated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what you changed in this part is already covered previous line 202

Copy link
Author

@raphaelzoellner raphaelzoellner Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous line 202 is the return of a manifest list if it could be fetched from the cache.

return true, &ManifestList{content, string(desc.Digest), contentType}, nil

But since manifests are not cached (in redis) this logs a not found error in previous line 190 for GET .../manifests/{tag} requests referencing a tag.

log.Debugf("Digest is not found in manifest list cache, key=cache:%v", manifestListKey(art.Repository, art))

and returns false (when a is nil, because the artifact with the matching digest in the local repository must not have an associated tag) and without a manifest in line 194.
return a != nil && string(desc.Digest) == a.Digest, nil, nil

This then leads to the manifest not being served from the local repository

if useLocal {
if man != nil {
w.Header().Set(contentLength, fmt.Sprintf("%v", len(man.Content)))
w.Header().Set(contentType, man.ContentType)
w.Header().Set(dockerContentDigest, man.Digest)
w.Header().Set(etag, man.Digest)
if r.Method == http.MethodGet {
_, err = w.Write(man.Content)
if err != nil {
return err
}
}
return nil
}
next.ServeHTTP(w, r)
return nil
}
log.Debugf("the tag is %v, digest is %v", art.Tag, art.Digest)
if r.Method == http.MethodHead {
err = proxyManifestHead(ctx, w, proxyCtl, p, art, remote)
} else if r.Method == http.MethodGet {
log.Warningf("Artifact: %v:%v, digest:%v is not found in proxy cache, fetch it from remote repo", art.Repository, art.Tag, art.Digest)
err = proxyManifestGet(ctx, w, proxyCtl, p, art, remote)
}

My intention with this part in new lines 223-234 was to check prior to the manifest list cache if a manifest with the digest already exists in the local repository and if found return it.

Wouldn't this extend the current behavior for that case?

Copy link
Author

@raphaelzoellner raphaelzoellner Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm new to harbor and I think my proposed implementation would skip other middlewares, which might be a problem.

// manifest
root.NewRoute().
Method(http.MethodGet).
Path("/*/manifests/:reference").
Middleware(metric.InjectOpIDMiddleware(metric.ManifestOperationID)).
Middleware(repoproxy.ManifestMiddleware()).
Middleware(contenttrust.ContentTrust()).
Middleware(vulnerable.Middleware()).
HandlerFunc(getManifest)
root.NewRoute().
Method(http.MethodHead).
Path("/*/manifests/:reference").
Middleware(metric.InjectOpIDMiddleware(metric.ManifestOperationID)).
Middleware(repoproxy.ManifestMiddleware()).
Middleware(contenttrust.ContentTrust()).
Middleware(vulnerable.Middleware()).
HandlerFunc(getManifest)

I will close this PR.

I think to solve this one would need to make sure that the initally proxied and cached artifact in the local repository will be referenced by the tag.

}

func manifestListKey(repo string, art lib.ArtifactInfo) string {
// actual redis key format is cache:manifestlist:<repo name>:<tag> or cache:manifestlist:<repo name>:sha256:xxxx
// actual redis key format is cache:manifest:<repo name>:<tag> or cache:manifest:<repo name>:sha256:xxxx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manifestListKey is only cache the manfest List, not used to cache a normal manifest

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, updating this comment was a mistake. I will revert it.

Signed-off-by: Raphael Zöllner <raphael.zoellner@regiocom.com>
Signed-off-by: Raphael Zöllner <raphael.zoellner@regiocom.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants