-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Create --append
flag to add file to existing artifact using podman artifact add
command
#25179
base: main
Are you sure you want to change the base?
Conversation
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: Honny1 The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
a23f466
to
1619f7f
Compare
1619f7f
to
1e16026
Compare
1e16026
to
cc2a56e
Compare
cc2a56e
to
3c5c832
Compare
@baude After the rebase, I reworked the code and extended the tests to test the |
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.
just some minor comments, mostly LGTM
pkg/libartifact/store/store.go
Outdated
@@ -29,6 +29,7 @@ import ( | |||
|
|||
var ( | |||
ErrEmptyArtifactName = errors.New("artifact name cannot be empty") | |||
SchemaVersion = 2 |
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.
This should be put into a const block, and maybe name it ManifestSchemaVersion
so we now want schema it is talking about.
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.
I’m not truly convinced this is worth making a constant. For specV1.Manifest.Version
, the only valid value is 2
, and that’s unlikely to change (a change would certainly mean a new MIME type and probably a new Go type).
It’s not a parameter that can be tuned without changing the type references.
The clean location where this constant should exist is somewhere in the opencontainers/image-spec
repo.
All that said, now that the code to declare a constant exists, I’m not going to ask that it should be replaced by a hard-coded 2
.
pkg/libartifact/store/store.go
Outdated
for _, path := range paths { | ||
fileName := filepath.Base(path) | ||
if _, ok := fileNames[fileName]; ok { | ||
return nil, fmt.Errorf("file: %s already exist in artifact", fileName) |
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.
return nil, fmt.Errorf("file: %s already exist in artifact", fileName) | |
return nil, fmt.Errorf("file %q already exist in artifact", fileName) |
pkg/libartifact/store/store.go
Outdated
index, err := as.readIndex() | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, manifest := range index.Manifests { | ||
if manifest.Annotations[specV1.AnnotationRefName] == artifact.Name { | ||
instanceDigest = &manifest.Digest | ||
break | ||
} | ||
} |
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.
Do we need to read the index? AFAICT artifact.GetDigest()
should return the same digest.
That said it seems GetDigest() reads the full manifest again and calculates the digest so using the index like you did might actually be the best thing here
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.
Yes, you're right when I tried to explain why I'm doing it there. I've found that my approach is not a good one.
test/e2e/artifact_test.go
Outdated
Expect(appendFail).Should(Exit(125)) | ||
Expect(appendFail.ErrorToString()).Should(Equal(fmt.Sprintf("Error: file: %s already exist in artifact", filepath.Base(artifact1File)))) |
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.
Not blocking but generally error checks should be combined with ExitWithError(code, "msg")
so it only takes one line and so it is not forgotten to check the error message.
same in the other places below
newLayer := specV1.Descriptor{ | ||
MediaType: detectedType, | ||
Digest: newBlobDigest, | ||
Size: newBlobSize, | ||
Annotations: annots, | ||
Annotations: annotations, |
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.
pre existing, what I just noticed in my extract PR is that this must copy the map here.
Reason is the map is shared so each iteration is overwriting the title and then all files end up with the same title. Looks like like we have a testing gap there, I am happy to push at as separate PR after this though.
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.
I moved the cloning of annotations to the loop where we insert files into the store. I'll add a check to test this 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.
3a89f36
to
cae7671
Compare
pkg/libartifact/store/store.go
Outdated
} | ||
|
||
for _, l := range lrs { | ||
if digest.String() == l.ManifestDescriptor.Digest.String() { |
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.
If two artifacts happen to have the same contents, and the same manifest (but differ in AnnotationRefName
), this would delete both, not just the one being updated.
AFAICS the NewReference
call always uses an AnnotationRefName
-based reference, and in that case PutManifest
replaces the previous manifest with the same name, already.
So, I think this only does something if there are two manifests with the same digest (== if this loses data). If the only manifest with oldDigest
was replaced in the index, it will not be found by this List
.
= I think this never does what we want.
I do think there is something we need to do — the original manifest blob should be deleted. But that should be an automatic part of imageDest.PutManifest
(or Commit?
), not something higher-level callers should need to do explicitly.
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.
PutManifest
takes the instanceDigest
argument. With this argument, we can replace the contents of the previous manifest. Still, the file will not be renamed so that the filename will be the original digest and the original digest will remain in the index.json repository.
In the case of adding multiple files to the artifact as separate runs of the command, the digest will need to be retrieved from index.json.
I think this is not a good approach. WDYT? @mtrmac
Current implementation: PutManifest
with nil
instanceDigest
creates a new manifest file and writes a new entry to index.json
. If the name of the new item already exists in index.json
, specV1.AnnotationRefName
is removed from the existing entry, and a new item with the name is inserted into index.json
. This causes two artifacts to exist, one with a name (the latest version) and one without a name (the previous name). Therefore, I decided to remove the artifact according to the digest. I will extend this by checking if an empty name is present. To ensure that there are no duplicate artifacts with different names.
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.
PutManifest
takes theinstanceDigest
argument. With this argument, we can replace the contents of the previous manifest.
I don’t understand this at all. (The instanceDigest
parameter exists to support multi-platform images, typically so that when writing “to a tag”, the tag is not moved by writes of the per-platform manifests, but only by the write of the final top-level one. It is both used by the generic c/image/copy
code, and a part of the public stable API; we can’t just redefine its meaning for one transport. [We could add a new transport-specific API, the way PutBlobFromLocalFile
was added for artifacts.])
Anyway, the implementation of PutManifest
knows whether/what it is replacing.
Still, the file will not be renamed so that the filename will be the original digest
That doesn’t work. The OCI data storage does not (apart from the top level) differentiate between manifests and data, it is possible for the same file (same digest) to be both a layer in one manifest, and a manifest elsewhere. The contents of the blob must match the digest.
Current implementation:
PutManifest
withnil
instanceDigest
creates a new manifest file and writes a new entry toindex.json
. If the name of the new item already exists inindex.json
,specV1.AnnotationRefName
is removed from the existing entry, and a new item with the name is inserted intoindex.json
. This causes two artifacts to exist, one with a name (the latest version) and one without a name (the previous name). Therefore, I decided to remove the artifact according to the digest. I will extend this by checking if an empty name is present. To ensure that there are no duplicate artifacts with different names.
That might be correct enough for the artifact store, but it is incorrect in general (the OCI layout store is not prohibited from containing N entries pointing to the same digest, some with no name).
Really, c/image/oci/layout
is responsible for keeping the layout consistent. And it, nowadays, has code to reference-count the blobs and to tell whether there are any other users, i.e. whether the overwritten manifest can/should be deleted.
cae7671
to
e4a3435
Compare
Fixes: https://issues.redhat.com/browse/RUN-2444 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
e4a3435
to
f29ac6a
Compare
This PR creates a
--append
flag to add a file to an existing artifact using thepodman artifact add
command.Fixes: https://issues.redhat.com/browse/RUN-2444
Does this PR introduce a user-facing change?