-
Notifications
You must be signed in to change notification settings - Fork 34
✨ unpacker: switch from google/go-containerregistry to containers/image #399
✨ unpacker: switch from google/go-containerregistry to containers/image #399
Conversation
Signed-off-by: Joe Lanford <[email protected]>
Signed-off-by: Joe Lanford <[email protected]>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #399 +/- ##
==========================================
+ Coverage 35.55% 38.04% +2.48%
==========================================
Files 17 15 -2
Lines 734 786 +52
==========================================
+ Hits 261 299 +38
Misses 443 443
- Partials 30 44 +14 ☔ View full report in Codecov by Sentry. |
| - op: add | ||
| path: /spec/template/spec/containers/1/args/- | ||
| value: "--ca-certs-dir=/var/certs" |
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.
Moved this to config/components/ca/patches/manager_deployment_cacerts.yaml (1) and changed the mount directory to /var/ca-certs (2). Why?
- I figured the setup of the CA was a better place to put the volume mount for that CA
- containers/image library complained about the original
/var/certscontaining the catalogd server crt/key, so this change puts just the CA in the/var/ca-certsdirectory separately.
| func NewUnrecoverable(err error) Unrecoverable { | ||
| return Unrecoverable{err} |
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.
Aligned this function with the similar change in operator-controller. There's no need for this to return a pointer. Returning a pointer here means that errors.As requires a double pointer, which is error prone.
internal/source/containers_image.go
Outdated
| if cleanupErr := os.RemoveAll(unpackPath); cleanupErr != nil { | ||
| err = errors.Join(err, cleanupErr) | ||
| } | ||
| _, isDigestBased := imgRef.(reference.Canonical) | ||
| return nil, wrapUnrecoverable(fmt.Errorf("error unpacking image: %w", err), isDigestBased) |
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 is something we may want to transfer back over to operator-controller.
| return err | ||
| } | ||
|
|
||
| func applyLayerFilter(srcPath string) archive.Filter { |
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/when we consolidate catalogd and operator-controller, it seems like we'll want this filter function to be configurable.
For now, this is updated from operator-controller's implementation to account for catalogd's ability to unpack just the relevant FBC root directory.
| cleanName := path.Clean(strings.TrimPrefix(h.Name, "/")) | ||
| relPath, err := filepath.Rel(cleanSrcPath, cleanName) | ||
| if err != nil { | ||
| return false, fmt.Errorf("error getting relative path: %w", err) | ||
| } | ||
| return relPath != ".." && !strings.HasPrefix(relPath, "../"), nil |
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 changed the implementation that catalogd had been using because I re-read it and couldn't convince myself if would work in unexpected, but still valid, scenarios.
I added a bunch of unit tests for this function to convince myself that this implementation is solid.
| assert.Contains(t, buf.String(), "found image in filesystem cache") | ||
| assert.Equal(t, tt.catalog.Status.ResolvedSource.Image.LastUnpacked, rs.ResolvedSource.Image.LastUnpacked) | ||
| assert.Contains(t, buf.String(), "image already unpacked") | ||
| assert.Equal(t, rs.ResolvedSource.Image.LastUnpacked.Time, unpackDirStat.ModTime()) |
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.
Instead of reading status, we now expect the mod time of the directory to match.
| assert.NotEqual(t, rs.ResolvedSource.Image.LastUnpacked.Time, oldDigestModTime) | ||
| assert.NoDirExists(t, oldDigestDir) |
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.
Instead of reading status, we now expect the new unpack time to be different than the mod time of the old digest.
We also expect the old digest dir to no longer exist.
| assert.Error(t, err) | ||
| var unrecov *catalogderrors.Unrecoverable | ||
| isUnrecov := errors.As(err, &unrecov) | ||
| isUnrecov := errors.As(err, &catalogderrors.Unrecoverable{}) |
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.
Changing this to remove the double-pointer thing.
| StateUnpacked State = "Unpacked" | ||
| ) | ||
|
|
||
| type unpacker struct { |
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.
No need for all of this extra abstraction since we no longer have alternate source types.
e7c87ae to
aef0190
Compare
|
|
||
| var unrecov *catalogderrors.Unrecoverable | ||
| if errors.As(reconcileErr, &unrecov) { | ||
| if errors.As(reconcileErr, &catalogderrors.Unrecoverable{}) { |
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.
Changing this to remove the double-pointer thing.
aef0190 to
3dd5765
Compare
|
/hold I want to update the e2e in a similar way that I did in operator-controller, where the image registry uses an untrusted cert and we mount an extra |
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.
Still need to review few more files. Will try to come back to this on Monday.
Going forward - can we please split large PRs into smaller ones as much as possible? For example build tag changes seem to be fairly independent here. It is easier to review small changes and smaller commits make code archaeology less challenging.
| ////////////////////////////////////////////////////// | ||
| // | ||
| // Resolve a canonical reference for the image. | ||
| // | ||
| ////////////////////////////////////////////////////// |
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.
Nit: I'm a fan of comments like this. Most of these sections look like they deserve own function.
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'll take a look to see if these can be function-ified. Only thing that comes to mind is if those functions would end up having to accept a bunch of parameters and return a bunch of values (and if so, maybe that wouldn't be much better?)
But good callout. I'll take a look.
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.
Updated! I made functions for:
- resolving the named and canonical refs
- loading the signature policy
There isn't really a lot more to squeeze out of this IMO. At this point, it's basically a series of steps with pretty straightforward error handling and basically no nesting, conditionals, or loops.
@everettraven asked for comments and structure along these lines when I was putting operator-controller's PR together because containers/image is a bit less ergonomic than google/go-containerregistry (I agree). So these comments are included to help explain what is actually going on.
I'm happy to reformat if there are other suggestions on style, but I do think the grouping and walkthrough-style narrative is helpful for the uninitiated.
3dd5765 to
58c39a8
Compare
| -X "$(VERSION_PKG).gitTreeState=$(GIT_TREE_STATE)" \ | ||
| -X "$(VERSION_PKG).commitDate=$(GIT_COMMIT_DATE)" | ||
| export GO_BUILD_GCFLAGS := all=-trimpath=${PWD} | ||
| export GO_BUILD_TAGS := containers_image_openpgp |
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.
@m1kola To answer your question about build tags: containers/image needs this build tag in order to build without requiring a third-party library that requires CGO.
I'm not sure it would make sense to split that into a separate PR.
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.
@joelanford I was thinking it was possible, for example, to introduce the plumbing for the build tags and add the actual tag in this PR. But that was just an example.
In general just asking (everyone) to be conscious about the PR sizes.
58c39a8 to
7af3c76
Compare
| func setReadOnlyRecursive(root string) error { | ||
| if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| fi, err := d.Info() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if err := func() error { | ||
| switch typ := fi.Mode().Type(); typ { | ||
| case os.ModeSymlink: | ||
| // do not follow symlinks | ||
| // 1. if they resolve to other locations in the root, we'll find them anyway | ||
| // 2. if they resolve to other locations outside the root, we don't want to change their permissions | ||
| return nil | ||
| case os.ModeDir: | ||
| return os.Chmod(path, 0500) | ||
| case 0: // regular file | ||
| return os.Chmod(path, 0400) | ||
| default: | ||
| return fmt.Errorf("refusing to change ownership of file %q with type %v", path, typ.String()) | ||
| } | ||
| }(); err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| }); err != nil { | ||
| return fmt.Errorf("error making catalog cache read-only: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func deleteRecursive(root string) error { | ||
| if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if !d.IsDir() { | ||
| return nil | ||
| } | ||
| if err := os.Chmod(path, 0700); err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| }); err != nil { | ||
| return fmt.Errorf("error making catalog cache writable for deletion: %w", err) | ||
| } | ||
| return os.RemoveAll(root) | ||
| } |
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.
Probably makes sense to port these back over to operator-controller and use them there as well.
23a6eff to
9878970
Compare
m1kola
left a comment
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 noticed one place where we duplicate if. Other than that - looks good.
I spent a lot of time staring at internal/source/containers_image.go. I think I understand what is going on there and didn't manage to spot any issues. But it might be good to have another pair of eyes on it.
| -X "$(VERSION_PKG).gitTreeState=$(GIT_TREE_STATE)" \ | ||
| -X "$(VERSION_PKG).commitDate=$(GIT_COMMIT_DATE)" | ||
| export GO_BUILD_GCFLAGS := all=-trimpath=${PWD} | ||
| export GO_BUILD_TAGS := containers_image_openpgp |
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.
@joelanford I was thinking it was possible, for example, to introduce the plumbing for the build tags and add the actual tag in this PR. But that was just an example.
In general just asking (everyone) to be conscious about the PR sizes.
Signed-off-by: Joe Lanford <[email protected]>
9878970 to
7b41ed3
Compare
m1kola
left a comment
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.
Re-approving after unused error check removed.
See:
I've structured this PR with a series of commits:
containers_image.godirectly from operator-controllerSigned-off-by: Joe Lanford [email protected]
Signed-off-by: Joe Lanford [email protected]