-
Notifications
You must be signed in to change notification settings - Fork 960
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
Download and cache package image contents in package manager #1795
Conversation
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.
cmd/crank/main.go
Outdated
ctx := kong.Parse(&cli, | ||
kong.Name("crank"), | ||
kong.Description("A tool for building platforms on Crossplane."), | ||
kong.Bind(child), |
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 binds child
, making it possible to pass it to command Run
functions.
cmd/crank/main.go
Outdated
// strChild is a string value child argument. | ||
type strChild string | ||
|
||
func (c strChild) AfterApply(arg *childArg) error { // nolint:unparam | ||
arg.strVal = string(c) | ||
return 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.
Any time an arg or flag of type strChild
is traversed in the node tree, AfterApply
will be invoked. I am using this to propagate values from child to parent commands.
cmd/crank/push.go
Outdated
} | ||
|
||
// Run runs the push cmd. | ||
func (c *pushCmd) Run(child *childArg) error { |
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.
Logic in the run function of a command with child node subcommands will be invoked after the subcommands are. This means that we can use common functionality in the parents and just use the child leaf nodes for value setting, etc.
Refactors crank <package-type> install <args> to crank install <package-type> <args> to more closely match kubectl semantics. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Refactors crank build <package-type> commands to build static packages from a scratch image using go-containerregistry. These packages are single layer with the layer being a tarball containing only a package.yaml, which has the contents of the package in a YAML stream. Note that the package images are saved with the .xpkg extension, which identifies them as Crossplane packages. Crossplane packages can be loaded into a container runtime, such as Docker, just as any OCI image tarball can. Importantly, Crossplane package images are not tagged on build, but are instead tagged on push before being written to a registry. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Refactors the crank push command to use go-containerregistry to write xpkg tarballs to registry. Also updates crank CLI name in usage to be kubectl crossplane such that users are not confused with command syntax. Note there is a large diff in the go.mod in this commit because we no longer rely on much of the docker machinery that was previously introduced. A few of the k8s dependencies were also updated because of the transitive dependencies in go-containerregistry. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Crossplane packages are opionated OCI image tarballs with the .xpkg extension. This pkg contains utilities for building, naming, and parsing both the on-disk crossplane package format and .xpkg tarballs directly. If other crossplane pkgs are moved to an internal directory in the future we should consider keeping this one public. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
The xpkg image cache is a filesystem-backed cache that can be used to store crossplane packages for controllers that require regular access to the packages. It is safe to pass to multiple concurrent controllers. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Package revisions will now cache their packages in the container filesystem instead of reading the logs of an install pod. This reference will no longer be used. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Now that install pods are no longer used by package revisions there is no need for the package controllers to create a pod only to fetch the digest of the package image. The pod manager is now replaced with a lighter weight digester which fetches an image digest used for generating a package revision's name. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Replaces the pod log parser backend with an image backend that first attempts to fetch an image from the xpkg cache, and if unsuccessful will retrieve it from a registry. The xpkg cache is pluggable, meaning that you can modify the image backend behavior by providing a cache with different properties. This is most clearly illustrated by providing a NopCache, which will cause the image backend to re-fetch the package image on every initialization. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Updates the package revision controllers to use the image backend for parsing and to apply a finalizer that is only removed after package contents are removed from the xpkg cache. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Build the package cache based on the cache-dir flag passed to crossplane and passes the cache to the package revision controllers. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Adds an emptyDir volume and mount to the crossplane deployment that is to be used for caching xpkg images. The emptyDir medium and size is configurable by passing values to the helm chart. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Updates the helm chart README and crossplane docs with info on how to configure the package cache. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
In order to allow for passing of more specific arguments to the push and build commands we use separate types for binding each. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Gives more informative errors and allows for overriding the filesystem used for retrieving and writing build artifacts. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Implements some basic tests that ensure an xpkg is written to memory mapped fs when command executes successfully. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Use the kong context bound stdout for writing such that it can be overridden during command tree parsing. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Plumbs fs from pushChild to push command such that it can be used with alternate file systems than just os.Fs. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Bumps crossplane-runtime to latest version on master in order to make use of the generic linter functionality in crank and the revision controllers. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Moves linter helpers that are specific to crossplane packages to the xpkg pkg and makes use of them alongside the crossplane-runtime linter functionality in the package revision controllers. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Extracts the fetching behavior of the image parser backend to a separate interface such that it is pluggable and can easily be tested. Also adds tests for image parser backend. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Updates build logic to accept parser and linter such that packages are validated when they are built. Build will fail if the wrong package type is supplied as a child command. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
The same meta and object schemes are almost always used for building crossplane packages, so they have been moved to public functions in the xpkg pkg. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
@negz I believe I have addressed all feedback from our synchronous review today, plus quite a bit more in terms of testing and API cleaning (including adding linting to the build commands). I still anticipate further improvements will be made in the future in terms of how we structure and test |
// TODO(hasheddan): re-enable after https://github.com/spf13/afero/pull/268 is merged. | ||
// "ErrEmptyImage": { | ||
// reason: "Should return error if image is empty.", | ||
// args: args{ | ||
// c: xpkg.NewNopCache(), | ||
// f: &MockFetcher{ | ||
// MockFetch: func() (v1.Image, error) { | ||
// return empty.Image, nil | ||
// }, | ||
// }, | ||
// opts: []parser.BackendOption{Package("test/test:latest")}, | ||
// }, | ||
// want: errors.Wrap(&os.PathError{Op: "open", Path: "package.yaml", Err: syscall.ENOENT}, errOpenPackageStream), | ||
// }, |
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 test causes a panic because of the way the afero is handling tarfs initialization when an empty tar reader is passed. I have opened spf13/afero#268 to fix. In practice, this would certainly be an edge case (i.e. totally empty package), but we obviously don't opportunity for panics in our code. I will work with afero maintainers to get this addressed prior to Crossplane release, or introduce a check and error early to avoid this execution branch.
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.
Awesome work @hasheddan! It's been a winding path, but I'm glad we landed here. This feels like a simplification. I've added a few comments but they're all nitpicks; feel free to ignore any you don't agree with or would prefer to defer, and merge at your leisure.
pkgName := child.name | ||
if pkgName == "" { | ||
metaPath := filepath.Join(root, xpkg.MetaFile) | ||
pkgName, err = xpkg.ParseNameFromMeta(child.fs, metaPath) |
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.
Is there any value in running these paths through filepath.Clean
? The gosec
linter has recently been recommending this to guard against http://cwe.mitre.org/data/definitions/22.html.
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.
@negz I believe filepath.Join()
also runs filepath.Clean()
on the returned result https://golang.org/pkg/path/filepath/#Join
objects Establisher | ||
parser parser.Parser | ||
linter parser.Linter | ||
backend parser.Backend |
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 wonder if parser
is the right name for the crossplane-runtime package. It's not immediately obvious what it's a parser for (i.e. packages), and parser.Parser
stutters. xpkg
would make sense if it didn't conflict with the local package of that name. Perhaps package.Parser
, package.Linter
, and package.Backend
?
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.
Yeah I struggled with this a bit. Similarly if it was named package
we would have package.Package
. I think parser
actually makes sense for Parser
(despite stuttering) and Backend
(i.e. it the backend for a parser). Linter
is linting the output of a parser, which would be a package so that is kind of strange. I guess we could name it pkg
, which would make the general crossplane-runtime library pkg
and the crossplane specific additions xpkg
... If we make this change I will likely do so in follow-up PR
_, err = io.Copy(buf, r) | ||
if err != nil { | ||
return nil, errors.Wrap(err, errCopyStream) | ||
} |
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: This (and the instance below) could be inlined to if _, err := ...; err != nil
. (Feels like there should be a linter for this.)
Changes flag type for package cache-dir to ExistingVarDir so that we panic early if cache directory does not exist. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Updates a few code comments and command outputs to be more descriptive of their functionality. Signed-off-by: hasheddan <georgedanielmangum@gmail.com>
Description of your changes
Updates the package manager to download static package images and cache them in a mounted volume. This PR will remain in draft while the following items are addressed:
I have:
make reviewable test
to ensure this PR is ready for review.How has this code been tested
Manual testing details to follow.