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

fix: make IMAGE_TAG available in buildArgs when used in docker FROM #9664

Open
wants to merge 33 commits into
base: main
Choose a base branch
from

Conversation

alphanota
Copy link
Contributor

@alphanota alphanota commented Jan 14, 2025

Note: This PR has the cherry-picked changes from @abe-winter 's original PR #9450.
Related:

Description
Apologies if I am simply misunderstanding something and this can be fixed w/out a code change. I am certainly not fully wrapping my head around this codebase.

This PR adds the ability to use IMAGE_TAG (IMAGE_REPO etc) in the docker buildArgs section, specifically when the build arg is then consumed in the Dockerfile FROM. On current stable and main, this case fails in the depList / SingleArtifactDependencies phase, with logs like:

Checking cache...

  • base: Found Remotely
  • child: Error checking cache.

getting hash for artifact "child": getting dependencies for "child": parsing ONBUILD instructions: retrieving image "gcr.io/headsdown/base:": parsing reference "gcr.io/headsdown/base:": could not parse reference: gcr.io/headsdown/base:

Note that when used with dependencies (as below), this seems to only work reliably if I run skaffold build && skaffold run. skaffold run by itself will sometimes fail with 'image not found remotely'.

Repro

Stripped-down skaffold.yaml:

build:
  local:
    useBuildkit: true
  artifacts:
    - image: base
      docker:
        dockerfile: Dockerfile
    - image: child
      requires:
      - image: base
      docker:
        dockerfile: child/Dockerfile
        buildArgs:
          IMAGE_TAG: '{{.IMAGE_TAG}}'

Top of child/Dockerfile:

# syntax=docker/dockerfile:1
ARG IMAGE_TAG
FROM gcr.io/PROJECTNAME/base:$IMAGE_TAG

tldr the second artifact depends on the first and then uses it as a base image

User facing changes
I think the docs already imply this is possible

Follow-up Work
No idea if I'm conforming to house style -- if accepted, guessing someone on the team will need to clean up or at least assign me cleanup. I would normally have submitted a bug rather than a PR for something like this, but I didn't understand why my template param was missing until I'd already solved the problem.

If this is something useful, happy to add tests etc.

@alphanota : We might want to add integration tests to check that the build args are resolved correctly.

@alphanota alphanota requested a review from a team as a code owner January 14, 2025 21:24
@alphanota alphanota requested a review from plumpy January 14, 2025 21:24
Copy link

@code-review-assist code-review-assist bot left a comment

Choose a reason for hiding this comment

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

Hi team,

I'm currently reviewing this pull request and will have a full review in a few minutes. In the meantime, here's a summary to help you and other reviewers quickly get up to speed:

This pull request addresses an issue where the IMAGE_TAG variable wasn't correctly available in buildArgs when used within a Docker FROM instruction. The problem manifested as errors during the dependency resolution phase of the build process, resulting in messages like could not parse reference: gcr.io/headsdown/base:<no value>.

Intent: The primary goal is to enable the use of IMAGE_TAG (and related variables like IMAGE_REPO) within buildArgs when those args are used to define the base image in a Dockerfile. This allows for more dynamic and flexible image building.

Changes:

The changes involve modifying several functions related to dependency listing and cache handling within the Skaffold build process. Specifically:

  • The DependencyLister function signature in pkg/skaffold/build/cache/cache.go was updated to include a tags map, allowing for the propagation of image tags.
  • The hash function in pkg/skaffold/build/cache/hash.go was modified to incorporate this tags map, ensuring that the cache correctly accounts for the image tag values.
  • Several other functions related to cache lookup and dependency resolution were updated to handle the tags map consistently, including pkg/skaffold/build/cache/lookup.go, pkg/skaffold/build/gcb/cloud_build.go, pkg/skaffold/diagnose/diagnose.go, and pkg/skaffold/runner/new.go.
  • A new helper function quickMakeEnvTags was added to pkg/skaffold/graph/dependencies.go to parse and create the tags map from an image reference.
  • The SingleArtifactDependencies function in pkg/skaffold/graph/dependencies.go was modified to accept and use the tags map.
  • The EvalBuildArgs function in pkg/skaffold/graph/dependencies.go was updated to EvalBuildArgsWithEnv, which takes the environment tags into account.

Activity: The pull request author, alphanota, provided a detailed description of the problem, including a reproduction scenario and a proposed solution. They also acknowledged potential style issues and expressed willingness to make further improvements.

I'll provide a more detailed review shortly, but this summary should give you a good overview of the changes and their purpose.

And now, a little haiku to brighten your day:

Tags flow, code compiles,
Building images, smooth and fast,
Skaffold's power shines.

Copy link

@code-review-assist code-review-assist bot left a comment

Choose a reason for hiding this comment

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

This PR addresses a critical issue where IMAGE_TAG (and related variables) were not available in buildArgs when used within the FROM instruction of a Dockerfile. The solution correctly propagates the tag information to the dependency resolution stage. However, the introduction of quickMakeEnvTags introduces a potential point of failure and adds complexity. I recommend exploring alternative approaches that leverage existing Skaffold mechanisms for resolving tags.

@alphanota alphanota closed this Jan 14, 2025
@alphanota alphanota deleted the abe-winter-changes branch January 14, 2025 21:28
@alphanota alphanota restored the abe-winter-changes branch January 14, 2025 21:29
@alphanota alphanota reopened this Jan 14, 2025
@pull-request-size pull-request-size bot added size/L and removed size/M labels Jan 16, 2025
Copy link
Collaborator

@plumpy plumpy left a comment

Choose a reason for hiding this comment

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

Ideally I'd love to see an integration test that uses these new args. But at the very least, there should be some unit tests.

@@ -80,7 +80,7 @@ func (h *artifactHasherImpl) hash(ctx context.Context, out io.Writer, a *latest.
}
hashes := []string{hash}
for _, dep := range sortedDependencies(a, h.artifacts) {
depHash, err := h.hash(ctx, out, dep, platforms)
depHash, err := h.hash(ctx, out, dep, platforms, tags)
Copy link
Collaborator

Choose a reason for hiding this comment

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

hash_test doesn't test this new behavior at all


// simpleMockArtifactResolver is an implementation of docker.ArtifactResolver
// that returns the same value for any key
type simpleMockArtifactResolver struct{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would probably call this stubArtifactResolver. Googling "stub vs mock" leads to 100s of pages with a variety of explanations, but here's a somewhat reasonable one:

A stub provides predetermined responses to calls made during a test. ... These responses are usually based on hard coded values or objects pre-programmed to return specific outputs, making them predictable.

Comment on lines +68 to +76
tag, ok := r.artifactResolver.GetImageTag(a.ImageName)
if !ok {
return nil, fmt.Errorf("unable to resolve tag for image: %s", a.ImageName)
}
envTags, err := EnvTags(tag)
if err != nil {
return nil, fmt.Errorf("unable to parse build args from tag %s: err %w", tag, err)
}
deps, err := r.SingleArtifactDependencies(ctx, a, envTags)
Copy link
Collaborator

Choose a reason for hiding this comment

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

there are no unit tests for this new behavior

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants