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 support for creating toolboxes from Containerfile #1401

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/toolbox-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolbox\-create - Create a new toolbox container
## SYNOPSIS
**toolbox create** [*--authfile FILE*]
[*--distro DISTRO* | *-d DISTRO*]
[*--file CONTAINERFILE* | *-f CONTAINERFILE*]
[*--image NAME* | *-i NAME*]
[*--release RELEASE* | *-r RELEASE*]
[*CONTAINER*]
Expand Down Expand Up @@ -95,6 +96,12 @@ Create a toolbox container for a different operating system DISTRO than the
host. Cannot be used with `--image`. Has to be coupled with `--release` unless
the selected DISTRO matches the host.

**--file** CONTAINERFILE, **-f** CONTAINERFILE

Create a toolbox container from a Containerfile. If used with `--image`, then
the built image will be tagged with that value, otherwise the current directory
name will be used.

**--image** NAME, **-i** NAME

Change the NAME of the image used to create the toolbox container. This is
Expand Down Expand Up @@ -136,6 +143,12 @@ $ toolbox create --image bar foo
$ toolbox create --authfile ~/auth.json --image registry.example.com/bar
```

### Create a custom toolbox container from a Containerfile

```
$ toolbox create --file Containerfile
```

## SEE ALSO

`toolbox(1)`, `toolbox-init-container(1)`, `podman(1)`, `podman-create(1)`, `podman-login(1)`, `podman-pull(1)`, `containers-auth.json(5)`
65 changes: 64 additions & 1 deletion src/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
Expand Down Expand Up @@ -47,6 +48,7 @@ var (
authFile string
container string
distro string
file string
image string
release string
}
Expand Down Expand Up @@ -87,6 +89,12 @@ func init() {
"",
"Create a toolbox container for a different operating system distribution than the host")

flags.StringVarP(&createFlags.file,
"file",
"f",
"",
"Create a toolbox container from the given Containerfile")

flags.StringVarP(&createFlags.image,
"image",
"i",
Expand Down Expand Up @@ -168,10 +176,43 @@ func create(cmd *cobra.Command, args []string) error {
containerArg = "--container"
}

image := createFlags.image

if cmd.Flag("file").Changed {
if !utils.PathExists(createFlags.file) {
Copy link
Member

Choose a reason for hiding this comment

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

I wish podman build would have a separate exit code for this. Even if the file exists here, there's no guarantee that it wouldn't disappear underneath us before the build starts. Filesystems are inherently racy.

Copy link
Member

Choose a reason for hiding this comment

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

I see a potential solution. Copy the contents of the file to a temporary location or into a buffer and pass that to podman build instead.

Copy link
Member

@debarshiray debarshiray Dec 4, 2023

Choose a reason for hiding this comment

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

Yes. I fiddled with podman build a bit more today. This works:

$ cat /path/to/build/context/Containerfile | podman build --file - /path/to/build/context

We could try to spot the Containerfile in the build context, then open it to get a io.Reader and feed it to shell.Run(...) as stdin when invoking podman build ....

It's worth bearing in mind that podman build supports specifying the build context directory as a HTTP URL of an archive, Git repository or a Containerfile, Containerfile.in that's supposed to be pre-processed by cpp(1), multiple Containerfiles and build contexts through a mix of --file, --build-context options and a [context] argument, etc..

Reading the code of the BuildDockerfiles() function in github.com/containers/buildah/imagebuildah, TempDirForURL() function in github.com/containers/buildah/define, and the podman build code leading up to them, I think we should only support a simple subset of what podman build offers. It will help us avoid fronting the entire podman build CLI in toolbox create, keep our error handling clean, and avoid race conditions that could lead to future (including security) bugs.

So, I propose supporting only a single build context that should contain a Container/Dockerfile. Supporting HTTP URLs for the context looks simple enough, but we should avoid the rest.

Copy link
Member

Choose a reason for hiding this comment

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

As a consequence of that, I think we should change --file to --image-build-context.

var builder strings.Builder
fmt.Fprintf(&builder, "file %s not found\n", createFlags.authFile)
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)

errMsg := builder.String()
return errors.New(errMsg)
}

var err error
if container == "" {
Copy link
Member

Choose a reason for hiding this comment

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

go fmt is unhappy about this line ...

container, err = dirname()
if err != nil {
return err
}
}
Copy link
Member

Choose a reason for hiding this comment

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

... and this one.


if image == "" {
image, err = dirname()
if err != nil {
return err
}
}

err = buildContainerImage(image, createFlags.file)
if err != nil {
return err
}
}

container, image, release, err := resolveContainerAndImageNames(container,
containerArg,
createFlags.distro,
createFlags.image,
image,
createFlags.release)

if err != nil {
Expand All @@ -185,6 +226,20 @@ func create(cmd *cobra.Command, args []string) error {
return nil
}

func buildContainerImage(image, containerFile string) error {
buildArgs := []string{
"build",
"--file",
containerFile,
"--tag",
image,
}
if err := shell.Run("podman", nil, nil, nil, buildArgs...); err != nil {
return fmt.Errorf("failed to build image %s", image)
}
return nil
}

func createContainer(container, image, release, authFile string, showCommandToEnter bool) error {
if container == "" {
panic("container not specified")
Expand Down Expand Up @@ -781,3 +836,11 @@ func systemdPathBusEscape(path string) string {
}
return string(n)
}

func dirname() (string, error) {
cwd, err := os.Getwd()
if err != nil {
return "", err
}
return path.Base(cwd), nil
}
Loading