Skip to content
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

Merged
merged 5 commits into from
Aug 11, 2021

Conversation

halvards
Copy link
Contributor

@halvards halvards commented Jun 22, 2021

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:

  • integration test
  • dependencies (for file watching)
  • insecure registries
  • debug mode
  • support for go flags and environment variables (based on Add support for Go build flags ko-build/ko#340)
  • actually plumbing the builder into the Skaffold CLI and API :-)

@halvards halvards requested a review from a team as a code owner June 22, 2021 13:19
@halvards halvards requested a review from nkubala June 22, 2021 13:19
@google-cla google-cla bot added the cla: yes label Jun 22, 2021
@codecov
Copy link

codecov bot commented Jun 22, 2021

Codecov Report

Merging #6054 (5158e66) into main (73d1ec9) will increase coverage by 0.02%.
The diff coverage is 74.28%.

Impacted file tree graph

@@            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     
Impacted Files Coverage Δ
pkg/skaffold/build/ko/build.go 60.00% <60.00%> (ø)
pkg/skaffold/build/ko/publisher.go 80.00% <80.00%> (ø)
pkg/skaffold/build/ko/builder.go 100.00% <100.00%> (ø)
pkg/skaffold/build/ko/ko.go 100.00% <100.00%> (ø)
pkg/skaffold/docker/parse.go 87.39% <0.00%> (-0.85%) ⬇️
pkg/skaffold/util/tar.go 65.51% <0.00%> (+2.29%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 73d1ec9...5158e66. Read the comment docs.

Copy link
Member

@briandealwis briandealwis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

pkg/skaffold/build/ko/build.go Show resolved Hide resolved
Comment on lines +80 to +88
// If the image name does _not_ start with `ko://`, determine the Go import
// path of the image workspace directory.
Copy link
Member

@briandealwis briandealwis Jun 29, 2021

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)

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Copy link
Member

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?

Copy link
Contributor Author

@halvards halvards Aug 5, 2021

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

)

// koImportPath is the import path of this package, with the ko scheme prefix.
const koImportPath = "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/ko"
Copy link
Member

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).

Copy link
Contributor Author

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).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #6286

pkg/skaffold/build/ko/schema/temporary.go Show resolved Hide resolved
pkg/skaffold/build/ko/schema/temporary.go Outdated Show resolved Hide resolved
pkg/skaffold/build/ko/schema/temporary.go Outdated Show resolved Hide resolved
halvards added a commit to halvards/ko that referenced this pull request Jul 2, 2021
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)
@tejal29 tejal29 added the kokoro:run runs the kokoro jobs on a PR label Jul 7, 2021
@kokoro-team kokoro-team removed the kokoro:run runs the kokoro jobs on a PR label Jul 7, 2021
@briandealwis briandealwis added the !! blocked !! this issue/PR is blocked by another issue label Jul 9, 2021
@briandealwis
Copy link
Member

Marking this as blocked pending ko-build/ko#378.

halvards added a commit to halvards/ko that referenced this pull request Jul 15, 2021
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)
jonjohnsonjr pushed a commit to ko-build/ko that referenced this pull request Jul 15, 2021
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)
@halvards
Copy link
Contributor Author

Friendly ping 🙂

@briandealwis briandealwis removed the !! blocked !! this issue/PR is blocked by another issue label Jul 23, 2021
pkg/skaffold/build/ko/build_test.go Show resolved Hide resolved
pkg/skaffold/build/ko/build_test.go Show resolved Hide resolved
importpath: "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/docker",
imageNameFromConfig: "ko://github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/docker",
workspace: "..",
},
Copy link
Member

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: ...
    },
},

Copy link
Contributor Author

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.

@halvards
Copy link
Contributor Author

I pushed a new commit with improvements to the confusing unit test.

docs/design_proposals/ko-builder.md Outdated Show resolved Hide resolved
Comment on lines +80 to +88
// If the image name does _not_ start with `ko://`, determine the Go import
// path of the image workspace directory.
Copy link
Member

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?

pkg/skaffold/build/ko/build_test.go Outdated Show resolved Hide resolved
pkg/skaffold/build/ko/build_test.go Outdated Show resolved Hide resolved
@halvards
Copy link
Contributor Author

halvards commented Aug 9, 2021

Friendly ping 😀

@MarlonGamez MarlonGamez added the kokoro:run runs the kokoro jobs on a PR label Aug 10, 2021
@kokoro-team kokoro-team removed the kokoro:run runs the kokoro jobs on a PR label Aug 10, 2021
Copy link
Contributor

@nkubala nkubala left a 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 :)

pkg/skaffold/build/ko/build.go Show resolved Hide resolved
pkg/skaffold/build/ko/build_test.go Show resolved Hide resolved
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`.
@halvards
Copy link
Contributor Author

Thanks for the review @briandealwis and @nkubala !

@tejal29 tejal29 merged commit e6a7a03 into GoogleContainerTools:main Aug 11, 2021
@halvards halvards deleted the ko-build-core branch August 12, 2021 00:41
halvards added a commit to halvards/skaffold that referenced this pull request Aug 16, 2021
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
halvards added a commit to halvards/skaffold that referenced this pull request Aug 16, 2021
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
tejal29 pushed a commit that referenced this pull request Aug 16, 2021
* 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.
halvards added a commit to halvards/skaffold that referenced this pull request Aug 17, 2021
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
tejal29 pushed a commit that referenced this pull request Aug 20, 2021
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: #6041
Related: #6054, #6437
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants