Skip to content

fix: stacker convert should check if FROMAS base is in current build - #2

Open
jmblatten wants to merge 1 commit into
mainfrom
fix-convert-FROMAS
Open

fix: stacker convert should check if FROMAS base is in current build#2
jmblatten wants to merge 1 commit into
mainfrom
fix-convert-FROMAS

Conversation

@jmblatten

Copy link
Copy Markdown
Collaborator
Currently, stacker converts the base part of a "FROM base AS name"
found in a dockerfile to stacker's from directive with type:docker
and url:docker.
However, the base could be a build stage in the current session.
In that case, stacker's from directive should be type:built and
tag:<something> instead.
This fix cycles thru the build stages to determine which are build stages and which are bases before converting and sets a flag.

What type of PR is this?
bug

Which issue does this PR fix:
756

What does this PR do / Why do we need it:
See above.

If an issue # is not available please add repro steps and logs showing the issue:
Issue 756
myhello.go file contents,

package main

import "fmt"

func main() {
   fmt.Println("hello world!")
}

Dockerfile contents:

FROM golang:1.22 AS mybuild
COPY myhello.go /src/myhello.go
WORKDIR /src
RUN export GOPATH=/go && export PATH=/go/bin:/usr/local/go/bin:$PATH && export HOME=/go && go build -o /bin/myhello ./myhello.go && ls /bin/myhello

FROM alpine AS mybase

FROM mybase AS A
COPY --from=mybuild /bin/myhello /bin/myhello

FROM A AS B
RUN chmod 755 /bin/myhello && \
    /bin/myhello

Resulting, converted stacker.yaml,

A:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker://mybase
  imports:
  - dest: /bin/myhello
    path: stacker://mybuild/bin/myhello
B:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker://A
  run:
  - sh -e -c 'chmod 755 /bin/myhello &&     /bin/myhello'
mybase:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker:alpine
mybuild:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker:golang:1.22
  imports:
  - dest: /src/myhello.go
    path: myhello.go
  run:
  - mkdir -p /src
  - cd /src
- sh -e -c 'export GOPATH=/go && export PATH=/go/bin:/usr/local/go/bin:$PATH &&
    export HOME=/go && go build -o /bin/myhello myhello.go && ls /bin/myhello'

Note that a stacker build will fail because there isn't a base layer A or B. They are instead stages in a build. Thus they perhaps should be represented as stages in the converted stacker file.

Testing done on this change:

  • CI
  • Manual:
    Same Dockerfile and myhello.go from above.

stacker with fix, resulting converted stacker.yaml,

A:
  build_env:
    arch: amd64
  from:
    tag: mybase
    type: built
  imports:
  - dest: /bin/myhello
    path: stacker://mybuild/bin/myhello
B:
  build_env:
    arch: amd64
  from:
    tag: A
    type: built
  run:
  - sh -e -c 'chmod 755 /bin/myhello &&     /bin/myhello'
mybase:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker:alpine
mybuild:
  build_env:
    arch: amd64
  from:
    type: docker
    url: docker:golang:1.22
  imports:
  - dest: /src/myhello.go
    path: myhello.go
  run:
  - mkdir -p /src
  - cd /src
  - sh -e -c 'export GOPATH=/go && export PATH=/go/bin:/usr/local/go/bin:$PATH &&
    export HOME=/go && go build -o /bin/myhello myhello.go && ls /bin/myhello'

Note that A and B are tagged while mybuild and mybase pull in base images. stacker build should work.

Automation added to e2e:
None

Will this break upgrades or downgrades?
No

Does this PR introduce any user-facing change?:
No, because previously the converted file would not build.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

    Currently, stacker converts the base part of a "FROM base AS name"
    found in a dockerfile to stacker's from directive with type:docker
    and url:docker.
    However, the base could be a build stage in the current session.
    In that case, stacker's from directive should be type:built and
    tag:<something> instead.

Signed-off-by: Joy Latten <joylatten@geico.com>
@jmblatten
jmblatten marked this pull request as ready for review July 28, 2026 18:39
Comment thread pkg/stacker/convert.go
Comment on lines +327 to +329
if fields[0] == "FROM" && len(fields) == 4 {
buildstages = append(buildstages, fields[3])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This works for parsing FROM <image> AS <name>, but FROM --platform=<platform> <image> AS <name> is equally valid. https://docs.docker.com/reference/dockerfile/#from

Does Stacker handle multi-platform image references? If so, we should handle that use-case here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would suggest len(fields) > 4 and fields[len(fields) - 1] which should cover both.

Comment thread pkg/stacker/convert.go
Comment on lines +76 to +77
var buildstages []string

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm not sure if creating a new package-level variable is appropriate here. Perhaps create a local variable in parseFile() and pass it into convertCommand() instead as an argument?

Comment thread pkg/stacker/convert.go
@@ -90,13 +93,22 @@ func (c *Converter) convertCommand(cmd *Command) error {
c.subs["IMAGE"] = "app"
} else if len(cmd.Value) == 3 && strings.EqualFold(cmd.Value[1], "as") {

@jsatterfield-geico jsatterfield-geico Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This tells me that Stacker does not support --platform <platform> in FROM, so maybe you don't need to support it yet.

Comment thread pkg/stacker/convert.go
}
// layer.BuildOnly = true // FIXME: should be enabled
} else {
return errors.Errorf("unsupported FROM directive")

@jsatterfield-geico jsatterfield-geico Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

And this tells me the --platform <platform> would be outright rejected. Please close the other two comments as resolved...

Comment thread pkg/stacker/convert.go

// Check if the base stage is in a build stage
if slices.Contains(buildstages, cmd.Value[0]) {
layer.From.Type = "built"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Instead of a raw string, I would use the Stacker-defined type: https://github.com/project-stacker/stacker/blob/main/pkg/types/layer.go#L25

Comment thread pkg/stacker/convert.go
layer.From.Type = "built"
layer.From.Tag = cmd.Value[0]
}
// layer.BuildOnly = true // FIXME: should be enabled

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Have you looked into enabling this flag versus setting layer.From? Is there a difference between the two? Just wondering because this comment implies that they do keep track of these "build-only" stages using this flag.

I'm wondering if setting layer.From directly maybe bypasses some of that logic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants