-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Closed
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,7 +58,7 @@ type Controller interface { | |
// UseLocalBlob check if the blob should use local copy | ||
UseLocalBlob(ctx context.Context, art lib.ArtifactInfo) bool | ||
// UseLocalManifest check manifest should use local copy | ||
UseLocalManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (bool, *ManifestList, error) | ||
UseLocalManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (bool, Manifests, error) | ||
// ProxyBlob proxy the blob request to the remote server, p is the proxy project | ||
// art is the ArtifactInfo which includes the digest of the blob | ||
ProxyBlob(ctx context.Context, p *proModels.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error) | ||
|
@@ -142,18 +142,56 @@ func (c *controller) UseLocalBlob(ctx context.Context, art lib.ArtifactInfo) boo | |
return exist | ||
} | ||
|
||
// Manifests is an interface implemented by Manifest and ManifestList | ||
type Manifests interface { | ||
Content() []byte | ||
Digest() string | ||
ContentType() string | ||
} | ||
|
||
// Manifest ... | ||
type Manifest struct { | ||
content []byte | ||
digest string | ||
contentType string | ||
} | ||
|
||
func (m *Manifest) Content() []byte { | ||
return m.content | ||
} | ||
|
||
func (m *Manifest) Digest() string { | ||
return m.digest | ||
} | ||
|
||
func (m *Manifest) ContentType() string { | ||
return m.contentType | ||
} | ||
|
||
// ManifestList ... | ||
type ManifestList struct { | ||
Content []byte | ||
Digest string | ||
ContentType string | ||
content []byte | ||
digest string | ||
contentType string | ||
} | ||
|
||
func (m *ManifestList) Content() []byte { | ||
return m.content | ||
} | ||
|
||
func (m *ManifestList) Digest() string { | ||
return m.digest | ||
} | ||
|
||
func (m *ManifestList) ContentType() string { | ||
return m.contentType | ||
} | ||
|
||
// UseLocalManifest check if these manifest could be found in local registry, | ||
// the return error should be nil when it is not found in local and need to delegate to remote registry | ||
// the return error should be NotFoundError when it is not found in remote registry | ||
// the error will be captured by framework and return 404 to client | ||
func (c *controller) UseLocalManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (bool, *ManifestList, error) { | ||
func (c *controller) UseLocalManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (bool, Manifests, error) { | ||
a, err := c.local.GetManifest(ctx, art) | ||
if err != nil { | ||
return false, nil, err | ||
|
@@ -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 | ||
// Pass digest to local repo | ||
// Pass digest to the cache key | ||
if len(art.Digest) == 0 { | ||
art.Digest = string(desc.Digest) | ||
} | ||
|
||
// attempt to retrieve manifest from local repo | ||
man, dig, err := c.local.PullManifest(art.Repository, art.Digest) | ||
if err == nil { | ||
log.Debugf("Got the manifest for repo: %v with digest: %v", art.Repository, dig) | ||
mediaType, payload, err := man.Payload() | ||
if err != nil { | ||
log.Errorf("Failed to get payload for manifest with digest: %v, error: %v", dig, err) | ||
} | ||
return true, &Manifest{content: payload, digest: dig, contentType: mediaType}, nil | ||
} | ||
log.Errorf("Failed to get manifest from local repo, error: %v", err) | ||
|
||
var content []byte | ||
var contentType string | ||
if c.cache == nil { | ||
return a != nil && string(desc.Digest) == a.Digest, nil, nil // digest matches | ||
} | ||
// Pass digest to the cache key, digest is more stable than tag, because tag could be updated | ||
if len(art.Digest) == 0 { | ||
art.Digest = string(desc.Digest) | ||
} | ||
|
||
// attempt to retrieve manifest list from cache | ||
err = c.cache.Fetch(ctx, manifestListKey(art.Repository, art), &content) | ||
if err != nil { | ||
if errors.Is(err, cache.ErrNotFound) { | ||
log.Debugf("Digest is not found in manifest list cache, key=cache:%v", manifestListKey(art.Repository, art)) | ||
} else { | ||
log.Errorf("Failed to get manifest list from cache, error: %v", err) | ||
if err == nil { | ||
// manifest list was found in cache | ||
err = c.cache.Fetch(ctx, manifestListContentTypeKey(art.Repository, art), &contentType) | ||
if err != nil { | ||
log.Debugf("failed to get the manifest list content type, not use local. error:%v", err) | ||
return false, nil, nil | ||
} | ||
return a != nil && string(desc.Digest) == a.Digest, nil, nil | ||
log.Debugf("Get the manifest list with key=cache:%v", manifestListKey(art.Repository, art)) | ||
return true, &ManifestList{content, string(desc.Digest), contentType}, nil | ||
} | ||
err = c.cache.Fetch(ctx, manifestListContentTypeKey(art.Repository, art), &contentType) | ||
if err != nil { | ||
log.Debugf("failed to get the manifest list content type, not use local. error:%v", err) | ||
return false, nil, nil | ||
if errors.Is(err, cache.ErrNotFound) { | ||
log.Debugf("Digest is not found in manifest list cache, key=cache:%v", manifestListKey(art.Repository, art)) | ||
} else { | ||
log.Errorf("Failed to get manifest list from cache, error: %v", err) | ||
} | ||
log.Debugf("Get the manifest list with key=cache:%v", manifestListKey(art.Repository, art)) | ||
return true, &ManifestList{content, string(desc.Digest), contentType}, nil | ||
|
||
// neither manifest was found in local repo nor manifest list was found in cache | ||
return a != nil && string(desc.Digest) == a.Digest, nil, nil | ||
} | ||
|
||
func manifestKey(repo string, art lib.ArtifactInfo) string { | ||
// actual redis key format is cache:manifest:<repo name>:<tag> or cache:manifest:<repo name>:sha256:xxxx | ||
return "manifest:" + repo + ":" + getReference(art) | ||
} | ||
|
||
func manifestContentTypeKey(rep string, art lib.ArtifactInfo) string { | ||
return manifestKey(rep, art) + ":contenttype" | ||
} | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, updating this comment was a mistake. I will revert it. |
||
return "manifestlist:" + repo + ":" + getReference(art) | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
harbor/src/controller/proxy/controller.go
Line 202 in 62d1d93
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.harbor/src/controller/proxy/controller.go
Line 190 in 62d1d93
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.harbor/src/controller/proxy/controller.go
Line 194 in 62d1d93
This then leads to the manifest not being served from the local repository
harbor/src/server/middleware/repoproxy/proxy.go
Lines 186 to 210 in 62d1d93
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?
There was a problem hiding this comment.
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.
harbor/src/server/registry/route.go
Lines 51 to 67 in 62d1d93
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.