-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add core ko builder implementation #6054
Conversation
Codecov Report
@@ Coverage Diff @@
## main #6054 +/- ##
==========================================
+ Coverage 70.38% 70.40% +0.02%
==========================================
Files 501 505 +4
Lines 22755 22825 +70
==========================================
+ Hits 16015 16070 +55
- Misses 5696 5702 +6
- Partials 1044 1053 +9
Continue to review full report at Codecov.
|
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.
Looking good!
// If the image name does _not_ start with `ko://`, determine the Go import | ||
// path of the image workspace directory. |
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.
Would it make sense to allow the user to specify the import-path? There are users with a monorepo that contains multiple images. So something like:
build:
artifacts:
- image: gcr.io/project/backend # ~my/image/one~
builder: ko
importPath: cmd/main1
- image: gcr.io/project/frontend # ~my/image/two~
builder: ko
importPath: cmd/main2
(implicit context: .
)
(Updated to use explicit and different image 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.
Yes! The idea is that users can pass this in the image name using the ko://
prefix like this:
build:
artifacts:
- image: ko://example.com/my/image/one
ko: {}
- image: ko://example.com/my/image/two
ko: {}
For the same context: .
. WDYT?
I'll add some unit tests to show this.
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 should happen for non-ko:// image references?
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.
Ko needs an import path to build an image. So for non-ko://
image references, getImportPath()
determines the Go import path from the context/workspace directory. This works with arbitrary image names in skaffold.yaml
- in other words, a non-ko://
image name doesn't need to match the import path.
The former supports current ko users moving to Skaffold, as they likely already have Kubernetes manifests with ko image references in place of image names. The latter matches Skaffold behavior for other builders, and would be more familiar for existing Skaffold users who want to start using ko.
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.
Added some unit tests to show non-cwd context/workspace builds.
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.
Using the
context
to specify the importPath is a bit unorthodox
The proposal uses context
to specify the directory of execution.
precludes users who use a different project layout such as with mixed languages
Does it? For the multi-go.mod
example, this configuration would work:
build:
artifacts:
- image: root
context: .
ko: {}
- image: pkg-foo
context: ./pkg/foo
ko: {}
The root
image in that example would use the go.mod
from the same directory as skaffold.yaml
, while the pkg-foo
image would use ./pkg/foo/go.mod
. The ko builder runs go list
and go build
in the context directory. This is the ko logic, the ko builder passes the context directory as the dir
argument to the moduleInfo()
function: https://github.com/google/ko/blob/690533235ad4209fab6c88f44314760b102ce829/pkg/build/gobuild.go#L153 and https://github.com/google/ko/blob/690533235ad4209fab6c88f44314760b102ce829/pkg/build/gobuild.go#L365
Full import paths (e.g., github.com/org/repo
aren't required in skaffold.yaml
, and I don't think we need to ask users to provide them. Supporting ko://
-prefixed import paths as image names in skaffold.yaml
is specifically just for existing ko users who use ko to render/populate image names in Kubernetes manifests. For these users, this feature simplifies Skaffold adoption, where Skaffold takes over rendering responsibilities, with no change required to their existing k8s manifest templates.
.ko.yaml
is only read from the current process working directory, or from KO_CONFIG_PATH
. It supports overriding the default base image, and the base image per import path prefix: https://github.com/google/ko#configuration
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 didn't explain myself well. Here the issue is:
build:
artifacts:
- image: gcr.io/k8s-project/image # this is the image name
context: . # use the root go.mod
How do I specify that the image should use the main() in ./pkg/foo
?
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 see. Given this directory structure:
./go.mod
./skaffold.yaml
./pkg/foo/main.go # <- main() is here
The Skaffold config would be:
build:
artifacts:
- image: gcr.io/k8s-project/image
context: ./pkg/foo
ko (and go build
) executes in the ./pkg/foo
directory, so context
specifies the directory of execution.
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 feels a little bit like a paradigm rift between the way we use context
for the other builders - if you tried to simply "plug and play" a different builder (e.g. GCB) using this specified context
, I don't think it would work since skaffold generally treats this as the root of all the required source files for building the artifact.
that said, since ko
is a little different I don't think we should keep blocking this implementation since we can always go back and change this later. I still think this would be clearer for users as an importPath
field in the build configuration (especially users who don't know anything about ko
except it's "a good way to build go apps"), but we can revisit as an enhancement 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.
Yes, you're right. It'll fail for non-local builders.
I'll dig into what the solution here could be and create another PR. Since users can specify import path via the image
field (as ko://import.path/here/pkg/foo
), adding an importPath
field would result in two ways to provide the import path, and that could be confusing.
Google Cloud Buildpacks fail if there are two main()
functions in different directories where both are not the context directory. There, the GOOGLE_BUILDABLE
envvar must be set: https://github.com/GoogleCloudPlatform/buildpacks/blob/main/README.md#configuration
pkg/skaffold/build/ko/build_test.go
Outdated
) | ||
|
||
// koImportPath is the import path of this package, with the ko scheme prefix. | ||
const koImportPath = "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/ko" |
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.
Aside: we seem to have broken this ko://
behaviour (#5505).
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 can take a look at this - feel free to assign me (I don't have permission to assign myself).
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.
Fixed in #6286
When embedding ko, it may be necessary to override the docker client. This adds a PublishOption to inject a docker client created elsewhere. Ko will use this client to interact with the docker daemon. Context: GoogleContainerTools/skaffold#6054 (comment)
Marking this as blocked pending ko-build/ko#378. |
When embedding ko, it may be necessary to override the docker client. This adds a PublishOption to inject a docker client created elsewhere. Ko will use this client to interact with the docker daemon. Context: GoogleContainerTools/skaffold#6054 (comment)
When embedding ko, it may be necessary to override the docker client. This adds a PublishOption to inject a docker client created elsewhere. Ko will use this client to interact with the docker daemon. Context: GoogleContainerTools/skaffold#6054 (comment)
Friendly ping 🙂 |
importpath: "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/docker", | ||
imageNameFromConfig: "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/docker", | ||
workspace: "..", | ||
}, |
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 found this test hard to puzzle through: it wasn't clear what are the inputs vs expected test results, and what are test incidentals. test.ref
seems an incidental? It might help to embed the Artifact definition in the test structure?
{
description: ...,
artifact: &latestV1.Artifact{
ArtifactType: latestV1.ArtifactType{KoArtifact: &latestV1.KoArtifact{}},
ImageName: "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/ko",
Workspace: ...
},
},
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 agree, it's not a great test. Since it's a unit test, I didn't want to actually build or publish - that's for the integration test.
That means the test covers wiring the components together, and a few bits of functionality: determining the Go import path, ensuring the image name is printed to the out
writer, and returning the image identifier correctly for pushed vs non-pushed images.
All conditional logic are in separate functions, so I'll replace this test with individual tests for those functions. I'll leave one test case for the Build()
function, just to check that everything is wired together.
I pushed a new commit with improvements to the confusing unit test. |
// If the image name does _not_ start with `ko://`, determine the Go import | ||
// path of the image workspace directory. |
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 didn't explain myself well. Here the issue is:
build:
artifacts:
- image: gcr.io/k8s-project/image # this is the image name
context: . # use the root go.mod
How do I specify that the image should use the main() in ./pkg/foo
?
Friendly ping 😀 |
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.
left a comment about the importPath
vs. context
, but I think we should move forward with this so it doesn't sit in limbo forever. one other nit about the temporary schema changes (which I think is a great idea) and otherwise this LGTM.
thanks @briandealwis for the thoughtful reviews on this and @halvards for sticking with us while we get it right :)
This adds the `Build()` method for building artifacts using ko. It supports both publishing the resulting image to a registry, and sideloading it to the local Docker daemon. The Skaffold docker client is used in the ko builder. This ensures that any Minikube config set by Skaffold is used. The ko builder uses the docker client when sideloading images to the docker daemon. The `temporary.go` file contains the structs intended to be added to the schema. The additions to the design proposal explain image naming for the ko builder, specifically how container images are named and how Go import paths are resolved when using the proposed ko builder. This commit includes ko builder unit tests for non-current-working-directory workspace dirs. These verify that the ko builder works even if the context specified in `skaffold.yaml` differs from the current working directory. This implementation is still missing the following features: - integration test - dependencies (for file watching) - insecure registries - debug mode - support for `go` flags and environment variables (based on ko-build/ko#340) - actually plumbing the builder into the Skaffold CLI and API :-) Tracking: GoogleContainerTools#6041
Split out the confusing end-to-end unit test with tests for individual functions.
Also fix typo in design proposal.
Reminders to update the import path once the contents of `pkg/skaffold/build/ko/schema` have been added to the real schema in `pkg/skaffold/schema/latest/v1`.
Thanks for the review @briandealwis and @nkubala ! |
e8a7e4e
to
84777c7
Compare
Adding the TODO in the import block resulted in gci linter errors: https://app.travis-ci.com/github/GoogleContainerTools/skaffold/jobs/530642848#L313
Decide on how ko builder users should specify the location of `package main` for their images. Previous discussion: GoogleContainerTools#6054 (comment) Tracking: GoogleContainerTools#6041 Related: GoogleContainerTools#6054, GoogleContainerTools#6286
Decide on how ko builder users should specify the location of `package main` for their images. Previous discussion: GoogleContainerTools#6054 (comment) Tracking: GoogleContainerTools#6041 Related: GoogleContainerTools#6054, GoogleContainerTools#6286
* Refine ko builder behavior for main packages Decide on how ko builder users should specify the location of `package main` for their images. Previous discussion: #6054 (comment) Tracking: #6041 Related: #6054, #6286 * Improve explanation of the Target config field Clarify how a blank value works, and also how users can use a pattern with wildcards.
This implements support for the Target config field for the ko builder. The Target field allows users to specify a target for `go build`. This is necessary where the main package is not in the context directory. Tracking: GoogleContainerTools#6041 Related: GoogleContainerTools#6054, GoogleContainerTools#6437
Description
This adds the
Build()
method for building artifacts using ko. It supports both publishing the resulting image to a registry, and sideloading it to the local Docker daemon.The Skaffold docker client is used in the ko builder. This ensures that any Minikube config set by Skaffold is used. The ko builder uses the docker client when sideloading images to the docker daemon.
The
temporary.go
file contains the structs intended to be added to the schema.The additions to the design proposal explain image naming for the ko builder, specifically how container images are named and how Go import paths are resolved when using the proposed ko builder.
This commit includes ko builder unit tests for non-current-working-directory workspace dirs. These verify that the ko builder works even if the context specified in
skaffold.yaml
differs from the current working directory.Tracking: #6041
Follow-up Work
This implementation is still missing the following features:
go
flags and environment variables (based on Add support for Go build flags ko-build/ko#340)