Merge pull request #14320 from flouthoc/build-honor-squash-and-layers

build: allow using `cache` explicitly with `--squash-all` using `--layers`
This commit is contained in:
OpenShift Merge Robot 2022-05-27 06:37:19 -04:00 committed by GitHub
commit a72b2402c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 843 additions and 181 deletions

View File

@ -197,9 +197,8 @@ func buildFlags(cmd *cobra.Command) {
// build executes the build command.
func build(cmd *cobra.Command, args []string) error {
if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) ||
(cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("layers")) ||
(cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) {
return errors.New("cannot specify --squash, --squash-all and --layers options together")
return errors.New("cannot specify --squash with --layers and --squash-all with --squash")
}
if cmd.Flag("output").Changed && registry.IsRemote() {
@ -418,8 +417,14 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
// Squash-all invoked, squash both new and old layers into one.
if c.Flags().Changed("squash-all") {
flags.Squash = true
if !c.Flags().Changed("layers") {
// Buildah supports using layers and --squash together
// after https://github.com/containers/buildah/pull/3674
// so podman must honor if user wants to still use layers
// with --squash-all.
flags.Layers = false
}
}
var stdin io.Reader
if flags.Stdin {
@ -442,22 +447,6 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return nil, err
}
// `buildah bud --layers=false` acts like `docker build --squash` does.
// That is all of the new layers created during the build process are
// condensed into one, any layers present prior to this build are retained
// without condensing. `buildah bud --squash` squashes both new and old
// layers down into one. Translate Podman commands into Buildah.
// Squash invoked, retain old layers, squash new layers into one.
if c.Flags().Changed("squash") && flags.Squash {
flags.Squash = false
flags.Layers = false
}
// Squash-all invoked, squash both new and old layers into one.
if c.Flags().Changed("squash-all") {
flags.Squash = true
flags.Layers = false
}
compression := buildahDefine.Gzip
if flags.DisableCompression {
compression = buildahDefine.Uncompressed
@ -513,9 +502,26 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return nil, errors.Wrapf(err, "unable to obtain decrypt config")
}
additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
if c.Flag("build-context").Changed {
for _, contextString := range flags.BuildContext {
av := strings.SplitN(contextString, "=", 2)
if len(av) > 1 {
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
if err != nil {
return nil, errors.Wrapf(err, "while parsing additional build context")
}
additionalBuildContext[av[0]] = &parseAdditionalBuildContext
} else {
return nil, fmt.Errorf("while parsing additional build context: %q, accepts value in the form of key=value", av)
}
}
}
opts := buildahDefine.BuildOptions{
AddCapabilities: flags.CapAdd,
AdditionalTags: tags,
AdditionalBuildContexts: additionalBuildContext,
AllPlatforms: flags.AllPlatforms,
Annotations: flags.Annotation,
Args: args,
@ -525,6 +531,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
Compression: compression,
ConfigureNetwork: networkPolicy,
ContextDirectory: contextDir,
CPPFlags: flags.CPPFlags,
DefaultMountsFilePath: containerConfig.Containers.DefaultMountsFile,
Devices: flags.Devices,
DropCapabilities: flags.CapDrop,

View File

@ -91,6 +91,33 @@ instructions read from the Containerfiles in the same way that environment
variables are, but which will not be added to environment variable list in the
resulting image's configuration.
#### **--build-context**=*name=value*
Specify an additional build context using its short name and its location.
Additional build contexts can be referenced in the same manner as we access
different stages in COPY instruction.
Valid values could be:
* Local directory e.g. --build-context project2=../path/to/project2/src
* HTTP URL to a tarball e.g. --build-context src=https://example.org/releases/src.tar
* Container image specified with a container-image:// prefix, e.g. --build-context alpine=container-image://alpine:3.15, (also accepts docker://, docker-image://)
On the Containerfile side, you can reference the build context on all
commands that accept the “from” parameter. Heres how that might look:
```dockerfile
FROM [name]
COPY --from=[name] ...
RUN --mount=from=[name] …
```
The value of [name] is matched with the following priority order:
* Named build context defined with --build-context [name]=..
* Stage defined with AS [name] inside Containerfile
* Image [name], either local or in a remote registry
#### **--cache-from**
Images to utilize as potential cache sources. Podman does not currently support
@ -140,6 +167,10 @@ This option is added to be aligned with other containers CLIs.
Podman doesn't communicate with a daemon or a remote server.
Thus, compressing the data before sending it is irrelevant to Podman. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
#### **--cpp-flag**=*flags*
Set additional flags to pass to the C Preprocessor cpp(1). Containerfiles ending with a ".in" suffix will be preprocessed via cpp(1). This option can be used to pass additional flags to cpp.Note: You can also set default CPPFLAGS by setting the BUILDAH_CPPFLAGS environment variable (e.g., export BUILDAH_CPPFLAGS="-DDEBUG").
#### **--cpu-period**=*limit*
Set the CPU period for the Completely Fair Scheduler (CFS), which is a

6
go.mod
View File

@ -11,13 +11,13 @@ require (
github.com/container-orchestrated-devices/container-device-interface v0.4.0
github.com/containernetworking/cni v1.1.0
github.com/containernetworking/plugins v1.1.1
github.com/containers/buildah v1.26.1
github.com/containers/buildah v1.26.1-0.20220524184833-5500333c2e06
github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471
github.com/containers/ocicrypt v1.1.4-0.20220428134531-566b808bdf6f
github.com/containers/psgo v1.7.2
github.com/containers/storage v1.41.1-0.20220511210719-cacc3325a9c8
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c
github.com/coreos/go-systemd/v22 v22.3.2
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/cyphar/filepath-securejoin v0.2.3

19
go.sum
View File

@ -268,7 +268,6 @@ github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE=
github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig=
github.com/containerd/containerd v1.6.4 h1:SEDZBp10mhCp+hkO3Njz/YhGrI7ah3edNcUlRdUPOgg=
github.com/containerd/containerd v1.6.4/go.mod h1:oWOqbuJUZmOVafhA0lj2NAXbiO1u7F0K5l1bUgdyo94=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@ -290,7 +289,6 @@ github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZH
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.4/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.5/go.mod h1:Rf2ZrMycr1El589IyuRzn7RkfdRZVKaFGaxSDHVAjj0=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
@ -337,17 +335,16 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD
github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE=
github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8=
github.com/containers/buildah v1.26.1 h1:D65Vuo+orsI14WWtJhSX6KrpgBBa7+hveVWevzG8p8E=
github.com/containers/buildah v1.26.1/go.mod h1:CsWSG8OpJd8v3mlLREJzVAOBgC93DjRNALUVHoi8QsY=
github.com/containers/common v0.48.0/go.mod h1:zPLZCfLXfnd1jI0QRsD4By54fP4k1+ifQs+tulIe3o0=
github.com/containers/buildah v1.26.1-0.20220524184833-5500333c2e06 h1:Tx1IfKch/SnsCk1YrdyR4B2AcS1TKLYxbSMXzmQXafU=
github.com/containers/buildah v1.26.1-0.20220524184833-5500333c2e06/go.mod h1:oB0PwsW+rhePNsBimCnEz4YMLx8QxZBjHi/DPnXhUCg=
github.com/containers/common v0.48.1-0.20220519181648-280c6f69fa82/go.mod h1:Ru/JjL1CTHzlxghVMhchzcFUwHLvlIeR5/SUMw8VUOI=
github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824 h1:5gMIUUpIK9DvHrrlj1Tik8GfCh5DEuVqm0JnYHWYUDw=
github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824/go.mod h1:Ru/JjL1CTHzlxghVMhchzcFUwHLvlIeR5/SUMw8VUOI=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.21.1/go.mod h1:zl35egpcDQa79IEXIuoUe1bW+D1pdxRxYjNlyb3YiXw=
github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4/go.mod h1:OsX9sFexyGF0FCNAjfcVFv3IwMqDyLyV/WQY/roLPcE=
github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059 h1:/FzsjrQ2nJtMom9IXEGieORlwUk/NyDuuz5SWcNo324=
github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059/go.mod h1:KntCBNQn3qOuZmQuJ38ORyTozmWXiuo05Vef2S0Sm5M=
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471 h1:2mm1jEFATvpdFfp8lUB/yc237OqwruMvfIPiVn1Wpgg=
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471/go.mod h1:KntCBNQn3qOuZmQuJ38ORyTozmWXiuo05Vef2S0Sm5M=
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU=
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@ -361,11 +358,10 @@ github.com/containers/psgo v1.7.2 h1:WbCvsY9w+nCv3j4der0mbD3PSRUv/W8l+G0YrZrdSDc
github.com/containers/psgo v1.7.2/go.mod h1:SLpqxsPOHtTqRygjutCPXmeU2PoEFzV3gzJplN4BMx0=
github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4=
github.com/containers/storage v1.38.0/go.mod h1:lBzt28gAk5ADZuRtwdndRJyqX22vnRaXmlF+7ktfMYc=
github.com/containers/storage v1.40.0/go.mod h1:zUyPC3CFIGR1OhY1CKkffxgw9+LuH76PGvVcFj38dgs=
github.com/containers/storage v1.40.2/go.mod h1:zUyPC3CFIGR1OhY1CKkffxgw9+LuH76PGvVcFj38dgs=
github.com/containers/storage v1.41.0/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s=
github.com/containers/storage v1.41.1-0.20220511210719-cacc3325a9c8 h1:4XdTbn3iVIr1+kN5srZND2G3/Q3hJiZSZZtKdL6r9jg=
github.com/containers/storage v1.41.1-0.20220511210719-cacc3325a9c8/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s=
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c h1:DQVf7UhxndNUtZ2+BIS/GtEdzszxMxrdqe43DRKRV2w=
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c/go.mod h1:HjV2DQuTFnjKYXDS3foE1EHODXu+dKHi7gT+uxT+kNk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -431,7 +427,6 @@ github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.3-0.20220208084023-a5c757555091+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.15+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ=
github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=

View File

@ -80,6 +80,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
CgroupParent string `schema:"cgroupparent"` // nolint
Compression uint64 `schema:"compression"`
ConfigureNetwork string `schema:"networkmode"`
CPPFlags string `schema:"cppflags"`
CpuPeriod uint64 `schema:"cpuperiod"` // nolint
CpuQuota int64 `schema:"cpuquota"` // nolint
CpuSetCpus string `schema:"cpusetcpus"` // nolint
@ -399,6 +400,15 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
// convert cppflags formats
var cppflags = []string{}
if _, found := r.URL.Query()["cppflags"]; found {
if err := json.Unmarshal([]byte(query.CPPFlags), &cppflags); err != nil {
utils.BadRequest(w, "cppflags", query.CPPFlags, err)
return
}
}
// convert nsoptions formats
nsoptions := buildah.NamespaceOptions{}
if _, found := r.URL.Query()["nsoptions"]; found {
@ -555,6 +565,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
AddCapabilities: addCaps,
AdditionalTags: additionalTags,
Annotations: annotations,
CPPFlags: cppflags,
Args: buildArgs,
AllPlatforms: query.AllPlatforms,
CommonBuildOpts: &buildah.CommonBuildOptions{

View File

@ -65,6 +65,14 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("annotations", l)
}
if cppflags := options.CPPFlags; len(cppflags) > 0 {
l, err := jsoniter.MarshalToString(cppflags)
if err != nil {
return nil, err
}
params.Set("cppflags", l)
}
if options.AllPlatforms {
params.Add("allplatforms", "1")
}

View File

@ -193,6 +193,22 @@ skip_if_remote "volumes don't work with podman-remote" \
"buildah bud --volume" \
"buildah-bud-policy"
# Most of this should work in podman remote after API implementation other than where context is host.
skip_if_remote "--build-context option not implemented in podman-remote" \
"build-with-additional-build-context and COPY, test pinning image" \
"build-with-additional-build-context and COPY, stagename and additional-context conflict" \
"build-with-additional-build-context and COPY, additionalContext and numeric value of stage" \
"build-with-additional-build-context and COPY, additionalContext and numeric value of stage" \
"build-with-additional-build-context and COPY, additional context from host" \
"build-with-additional-build-context and COPY, additional context from external URL" \
"build-with-additional-build-context and RUN --mount=from=, additional-context is URL and mounted from subdir" \
"build-with-additional-build-context and RUN --mount=from=, additional-context not image and also test conflict with stagename" \
"build-with-additional-build-context and RUN --mount=from=, additional-context and also test conflict with stagename" \
"bud-multiple-platform for --all-platform with additional-build-context" \
"build-with-additional-build-context and FROM, stagename and additional-context conflict" \
"bud with Containerfile.in, via envariable" \
"build-with-additional-build-context and FROM, pin busybox to alpine"
# Requires a local file outside context dir
skip_if_remote "local keyfile not sent to podman-remote" \
"bud with encrypted FROM image"

View File

@ -178,6 +178,32 @@ var _ = Describe("Podman build", func() {
Expect(session).Should(Exit(0))
})
It("podman build verify explicit cache use with squash-all and --layers", func() {
session := podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-c", "--squash-all", "--layers", "-t", "test-squash-d:latest", "build/squash"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-d"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// Check for one layers
Expect(strings.Fields(session.OutputToString())).To(HaveLen(1))
// Second build must use last squashed build from cache
session = podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-c", "--squash-all", "--layers", "-t", "test", "build/squash"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// Test if entire build is used from cache
Expect(session.OutputToString()).To(ContainSubstring("Using cache"))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-d"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// Check for one layers
Expect(strings.Fields(session.OutputToString())).To(HaveLen(1))
})
It("podman build Containerfile locations", func() {
// Given
// Switch to temp dir and restore it afterwards

View File

@ -2,11 +2,6 @@
# Changelog
## v1.26.1 (2022-05-04)
Make `buildah build --label foo` create an empty "foo" label again
Bump to v1.27.0-dev
## v1.26.0 (2022-05-04)
imagebuildah,build: move deepcopy of args before we spawn goroutine

View File

@ -1,7 +1,3 @@
- Changelog for v1.26.1 (2022-05-04)
* Make `buildah build --label foo` create an empty "foo" label again
* Bump to v1.27.0-dev
- Changelog for v1.26.0 (2022-05-04)
* imagebuildah,build: move deepcopy of args before we spawn goroutine
* Vendor in containers/storage v1.40.2

View File

@ -40,14 +40,6 @@ const (
func init() {
reexec.Register(copierCommand, copierMain)
// Attempt a user and host lookup to force libc (glibc, and possibly others that use dynamic
// modules to handle looking up user and host information) to load modules that match the libc
// our binary is currently using. Hopefully they're loaded on first use, so that they won't
// need to be loaded after we've chrooted into the rootfs, which could include modules that
// don't match our libc and which can't be loaded, or modules which we don't want to execute
// because we don't trust their code.
_, _ = user.Lookup("buildah")
_, _ = net.LookupHost("localhost")
}
// isArchivePath returns true if the specified path can be read like a (possibly
@ -712,6 +704,15 @@ func copierMain() {
encoder := json.NewEncoder(os.Stdout)
previousRequestRoot := ""
// Attempt a user and host lookup to force libc (glibc, and possibly others that use dynamic
// modules to handle looking up user and host information) to load modules that match the libc
// our binary is currently using. Hopefully they're loaded on first use, so that they won't
// need to be loaded after we've chrooted into the rootfs, which could include modules that
// don't match our libc and which can't be loaded, or modules which we don't want to execute
// because we don't trust their code.
_, _ = user.Lookup("buildah")
_, _ = net.LookupHost("localhost")
// Set logging.
if level := os.Getenv("LOGLEVEL"); level != "" {
if ll, err := strconv.Atoi(level); err == nil {

View File

@ -11,6 +11,21 @@ import (
"golang.org/x/sync/semaphore"
)
// AdditionalBuildContext contains verbose details about a parsed build context from --build-context
type AdditionalBuildContext struct {
// Value is the URL of an external tar archive.
IsURL bool
// Value is the name of an image which may or may not have already been pulled.
IsImage bool
// Value holds a URL, an image name, or an absolute filesystem path.
Value string
// Absolute filesystem path to downloaded and exported build context
// from external tar archive. This will be populated only if following
// buildcontext is created from IsURL and was downloaded before in any
// of the RUN step.
DownloadedCache string
}
// CommonBuildOptions are resources that can be defined by flags for both buildah from and build
type CommonBuildOptions struct {
// AddHost is the list of hostnames to add to the build container's /etc/hosts.
@ -121,6 +136,8 @@ type BuildOptions struct {
Compression archive.Compression
// Arguments which can be interpolated into Dockerfiles
Args map[string]string
// Map of external additional build contexts
AdditionalBuildContexts map[string]*AdditionalBuildContext
// Name of the image to write to.
Output string
// BuildOutput specifies if any custom build output is selected for following build.
@ -187,6 +204,8 @@ type BuildOptions struct {
DropCapabilities []string
// CommonBuildOpts is *required*.
CommonBuildOpts *CommonBuildOptions
// CPPFlags are additional arguments to pass to the C Preprocessor (cpp).
CPPFlags []string
// DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format
DefaultMountsFilePath string
// IIDFile tells the builder to write the image ID to the specified file

View File

@ -29,7 +29,7 @@ const (
Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec
// too.
Version = "1.26.1"
Version = "1.27.0-dev"
// DefaultRuntime if containers.conf fails.
DefaultRuntime = "runc"
@ -127,13 +127,18 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
return "", "", errors.Wrapf(err, "error parsing url %q", url)
}
if strings.HasPrefix(url, "git://") || strings.HasSuffix(urlParsed.Path, ".git") {
combinedOutput, err := cloneToDirectory(url, name)
combinedOutput, gitSubDir, err := cloneToDirectory(url, name)
if err != nil {
if err2 := os.RemoveAll(name); err2 != nil {
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
}
return "", "", errors.Wrapf(err, "cloning %q to %q:\n%s", url, name, string(combinedOutput))
}
// Check if git url specifies any subdir
// if subdir is there switch to subdir.
if gitSubDir != "" {
name = filepath.Join(name, gitSubDir)
}
return name, "", nil
}
if strings.HasPrefix(url, "github.com/") {
@ -170,17 +175,29 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
return "", "", errors.Errorf("unreachable code reached")
}
func cloneToDirectory(url, dir string) ([]byte, error) {
gitBranch := strings.Split(url, "#")
func cloneToDirectory(url, dir string) ([]byte, string, error) {
gitSubdir := ""
gitBranch := ""
gitBranchPart := strings.Split(url, "#")
var cmd *exec.Cmd
if len(gitBranch) < 2 {
logrus.Debugf("cloning %q to %q", url, dir)
cmd = exec.Command("git", "clone", url, dir)
} else {
logrus.Debugf("cloning repo %q and branch %q to %q", gitBranch[0], gitBranch[1], dir)
cmd = exec.Command("git", "clone", "--recurse-submodules", "-b", gitBranch[1], gitBranch[0], dir)
if len(gitBranchPart) > 1 {
// check if string contains path to a subdir
gitSubDirPart := strings.Split(gitBranchPart[1], ":")
if len(gitSubDirPart) > 1 {
gitSubdir = gitSubDirPart[1]
}
return cmd.CombinedOutput()
gitBranch = gitSubDirPart[0]
}
if gitBranch == "" {
logrus.Debugf("cloning %q to %q", gitBranchPart[0], dir)
cmd = exec.Command("git", "clone", "--recurse-submodules", gitBranchPart[0], dir)
} else {
logrus.Debugf("cloning repo %q and branch %q to %q", gitBranchPart[0], gitBranch, dir)
cmd = exec.Command("git", "clone", "--recurse-submodules", "-b", gitBranch, gitBranchPart[0], dir)
}
combinedOutput, err := cmd.CombinedOutput()
return combinedOutput, gitSubdir, err
}
func downloadToDirectory(url, dir string) error {

View File

@ -5,12 +5,12 @@ go 1.16
require (
github.com/containerd/containerd v1.6.4
github.com/containernetworking/cni v1.1.0
github.com/containers/common v0.48.0
github.com/containers/image/v5 v5.21.1
github.com/containers/common v0.48.1-0.20220519181648-280c6f69fa82
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471
github.com/containers/ocicrypt v1.1.4-0.20220428134531-566b808bdf6f
github.com/containers/storage v1.40.2
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c
github.com/docker/distribution v2.8.1+incompatible
github.com/docker/docker v20.10.14+incompatible
github.com/docker/docker v20.10.16+incompatible
github.com/docker/go-units v0.4.0
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316
github.com/fsouza/go-dockerclient v1.7.11
@ -23,7 +23,7 @@ require (
github.com/onsi/gomega v1.19.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84
github.com/opencontainers/runc v1.1.1
github.com/opencontainers/runc v1.1.2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/selinux v1.10.1

View File

@ -217,7 +217,6 @@ github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE=
github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig=
github.com/containerd/containerd v1.6.4 h1:SEDZBp10mhCp+hkO3Njz/YhGrI7ah3edNcUlRdUPOgg=
github.com/containerd/containerd v1.6.4/go.mod h1:oWOqbuJUZmOVafhA0lj2NAXbiO1u7F0K5l1bUgdyo94=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@ -239,7 +238,6 @@ github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZH
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.4/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
github.com/containerd/go-cni v1.1.5/go.mod h1:Rf2ZrMycr1El589IyuRzn7RkfdRZVKaFGaxSDHVAjj0=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
@ -285,10 +283,11 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD
github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE=
github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8=
github.com/containers/common v0.48.0 h1:997nnXBZ+eNpfSM7L4SxhhZubQrfEyw3jRyNMTSsNlw=
github.com/containers/common v0.48.0/go.mod h1:zPLZCfLXfnd1jI0QRsD4By54fP4k1+ifQs+tulIe3o0=
github.com/containers/image/v5 v5.21.1 h1:Cr3zw2f0FZs4SCkdGlc8SN/mpcmg2AKG4OUuDbeGS/Q=
github.com/containers/image/v5 v5.21.1/go.mod h1:zl35egpcDQa79IEXIuoUe1bW+D1pdxRxYjNlyb3YiXw=
github.com/containers/common v0.48.1-0.20220519181648-280c6f69fa82 h1:+FcjjNdCzhLp9jmkkZJ9wxqGwFtQVlKKDR/GWHwTOXY=
github.com/containers/common v0.48.1-0.20220519181648-280c6f69fa82/go.mod h1:Ru/JjL1CTHzlxghVMhchzcFUwHLvlIeR5/SUMw8VUOI=
github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4/go.mod h1:OsX9sFexyGF0FCNAjfcVFv3IwMqDyLyV/WQY/roLPcE=
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471 h1:2mm1jEFATvpdFfp8lUB/yc237OqwruMvfIPiVn1Wpgg=
github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471/go.mod h1:KntCBNQn3qOuZmQuJ38ORyTozmWXiuo05Vef2S0Sm5M=
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU=
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@ -299,9 +298,10 @@ github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pA
github.com/containers/ocicrypt v1.1.4-0.20220428134531-566b808bdf6f h1:hffElEaoDQfREHltc2wtFPd68BqDmzW6KkEDpuSRBjs=
github.com/containers/ocicrypt v1.1.4-0.20220428134531-566b808bdf6f/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g=
github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4=
github.com/containers/storage v1.40.0/go.mod h1:zUyPC3CFIGR1OhY1CKkffxgw9+LuH76PGvVcFj38dgs=
github.com/containers/storage v1.40.2 h1:GUlHaGnrs1JOEwv6YEvkQdgYXOXZdU1Angy4wgWNgF8=
github.com/containers/storage v1.40.2/go.mod h1:zUyPC3CFIGR1OhY1CKkffxgw9+LuH76PGvVcFj38dgs=
github.com/containers/storage v1.41.0/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s=
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c h1:DQVf7UhxndNUtZ2+BIS/GtEdzszxMxrdqe43DRKRV2w=
github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c/go.mod h1:HjV2DQuTFnjKYXDS3foE1EHODXu+dKHi7gT+uxT+kNk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -353,8 +353,9 @@ github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.3-0.20220208084023-a5c757555091+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.14+incompatible h1:+T9/PRYWNDo5SZl5qS1r9Mo/0Q8AwxKKPtu9S1yxM0w=
github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.15+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ=
github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
@ -631,8 +632,9 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw=
github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ=
github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -773,8 +775,9 @@ github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runc v1.1.1 h1:PJ9DSs2sVwE0iVr++pAHE6QkS9tzcVWozlPifdwMgrU=
github.com/opencontainers/runc v1.1.1/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@ -811,8 +814,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/proglottis/gpgme v0.1.1 h1:72xI0pt/hy7pqsRxk32KExITkXp+RZErRizsA+up/lQ=
github.com/proglottis/gpgme v0.1.1/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0=
github.com/proglottis/gpgme v0.1.2 h1:dKlhDqJ0kdEt+YHCD8FQEUdF9cJj/+mbJUNyUGNAEzY=
github.com/proglottis/gpgme v0.1.2/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=

View File

@ -28,6 +28,7 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/hashicorp/go-multierror"
"github.com/mattn/go-shellwords"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/openshift/imagebuilder"
@ -157,7 +158,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B
// pre-process Dockerfiles with ".in" suffix
if strings.HasSuffix(dfile, ".in") {
pData, err := preprocessContainerfileContents(logger, dfile, data, options.ContextDirectory)
pData, err := preprocessContainerfileContents(logger, dfile, data, options.ContextDirectory, options.CPPFlags)
if err != nil {
return "", nil, err
}
@ -211,7 +212,10 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B
}
if options.AllPlatforms {
options.Platforms, err = platformsForBaseImages(ctx, logger, paths, files, options.From, options.Args, options.SystemContext)
if options.AdditionalBuildContexts == nil {
options.AdditionalBuildContexts = make(map[string]*define.AdditionalBuildContext)
}
options.Platforms, err = platformsForBaseImages(ctx, logger, paths, files, options.From, options.Args, options.AdditionalBuildContexts, options.SystemContext)
if err != nil {
return "", nil, err
}
@ -467,7 +471,7 @@ func warnOnUnsetBuildArgs(logger *logrus.Logger, node *parser.Node, args map[str
// preprocessContainerfileContents runs CPP(1) in preprocess-only mode on the input
// dockerfile content and will use ctxDir as the base include path.
func preprocessContainerfileContents(logger *logrus.Logger, containerfile string, r io.Reader, ctxDir string) (stdout io.Reader, err error) {
func preprocessContainerfileContents(logger *logrus.Logger, containerfile string, r io.Reader, ctxDir string, cppFlags []string) (stdout io.Reader, err error) {
cppCommand := "cpp"
cppPath, err := exec.LookPath(cppCommand)
if err != nil {
@ -480,7 +484,16 @@ func preprocessContainerfileContents(logger *logrus.Logger, containerfile string
stdoutBuffer := bytes.Buffer{}
stderrBuffer := bytes.Buffer{}
cmd := exec.Command(cppPath, "-E", "-iquote", ctxDir, "-traditional", "-undef", "-")
cppArgs := []string{"-E", "-iquote", ctxDir, "-traditional", "-undef", "-"}
if flags, ok := os.LookupEnv("BUILDAH_CPPFLAGS"); ok {
args, err := shellwords.Parse(flags)
if err != nil {
return nil, errors.Errorf("error parsing BUILDAH_CPPFLAGS %q: %v", flags, err)
}
cppArgs = append(cppArgs, args...)
}
cppArgs = append(cppArgs, cppFlags...)
cmd := exec.Command(cppPath, cppArgs...)
cmd.Stdin = r
cmd.Stdout = &stdoutBuffer
cmd.Stderr = &stderrBuffer
@ -502,8 +515,8 @@ func preprocessContainerfileContents(logger *logrus.Logger, containerfile string
// platformsForBaseImages resolves the names of base images from the
// dockerfiles, and if they are all valid references to manifest lists, returns
// the list of platforms that are supported by all of the base images.
func platformsForBaseImages(ctx context.Context, logger *logrus.Logger, dockerfilepaths []string, dockerfiles [][]byte, from string, args map[string]string, systemContext *types.SystemContext) ([]struct{ OS, Arch, Variant string }, error) {
baseImages, err := baseImages(dockerfilepaths, dockerfiles, from, args)
func platformsForBaseImages(ctx context.Context, logger *logrus.Logger, dockerfilepaths []string, dockerfiles [][]byte, from string, args map[string]string, additionalBuildContext map[string]*define.AdditionalBuildContext, systemContext *types.SystemContext) ([]struct{ OS, Arch, Variant string }, error) {
baseImages, err := baseImages(dockerfilepaths, dockerfiles, from, args, additionalBuildContext)
if err != nil {
return nil, errors.Wrapf(err, "determining list of base images")
}
@ -631,7 +644,7 @@ func platformsForBaseImages(ctx context.Context, logger *logrus.Logger, dockerfi
// stage's base image with FROM, and returns the list of base images as
// provided. Each entry in the dockerfilenames slice corresponds to a slice in
// dockerfilecontents.
func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from string, args map[string]string) ([]string, error) {
func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from string, args map[string]string, additionalBuildContext map[string]*define.AdditionalBuildContext) ([]string, error) {
mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0]))
if err != nil {
return nil, errors.Wrapf(err, "error parsing main Dockerfile: %s", dockerfilenames[0])
@ -670,6 +683,13 @@ func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from stri
child.Next.Value = from
from = ""
}
if replaceBuildContext, ok := additionalBuildContext[child.Next.Value]; ok {
if replaceBuildContext.IsImage {
child.Next.Value = replaceBuildContext.Value
} else {
return nil, fmt.Errorf("build context %q is not an image, can not be used for FROM %q", child.Next.Value, child.Next.Value)
}
}
base := child.Next.Value
if base != "scratch" && !nicknames[base] {
// TODO: this didn't undergo variable and arg

View File

@ -126,6 +126,7 @@ type Executor struct {
imageInfoLock sync.Mutex
imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs
fromOverride string
additionalBuildContexts map[string]*define.AdditionalBuildContext
manifest string
secrets map[string]define.Secret
sshsources map[string]*sshagent.Source
@ -275,6 +276,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
rusageLogFile: rusageLogFile,
imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs),
fromOverride: options.From,
additionalBuildContexts: options.AdditionalBuildContexts,
manifest: options.Manifest,
secrets: secrets,
sshsources: sshsources,
@ -609,6 +611,12 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
}
base := child.Next.Value
if base != "scratch" {
if replaceBuildContext, ok := b.additionalBuildContexts[child.Next.Value]; ok {
if replaceBuildContext.IsImage {
child.Next.Value = replaceBuildContext.Value
base = child.Next.Value
}
}
userArgs := argsMapToSlice(stage.Builder.Args)
baseWithArg, err := imagebuilder.ProcessWord(base, userArgs)
if err != nil {

View File

@ -369,6 +369,52 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
if fromErr != nil {
return errors.Wrapf(fromErr, "unable to resolve argument %q", copy.From)
}
var additionalBuildContext *define.AdditionalBuildContext
if foundContext, ok := s.executor.additionalBuildContexts[from]; ok {
additionalBuildContext = foundContext
} else {
// Maybe index is given in COPY --from=index
// if that's the case check if provided index
// exists and if stage short_name matches any
// additionalContext replace stage with addtional
// build context.
if _, err := strconv.Atoi(from); err == nil {
if stage, ok := s.executor.stages[from]; ok {
if foundContext, ok := s.executor.additionalBuildContexts[stage.name]; ok {
additionalBuildContext = foundContext
}
}
}
}
if additionalBuildContext != nil {
if !additionalBuildContext.IsImage {
contextDir = additionalBuildContext.Value
if additionalBuildContext.IsURL {
// Check if following buildContext was already
// downloaded before in any other RUN step. If not
// download it and populate DownloadCache field for
// future RUN steps.
if additionalBuildContext.DownloadedCache == "" {
// additional context contains a tar file
// so download and explode tar to buildah
// temp and point context to that.
path, subdir, err := define.TempDirForURL(internalUtil.GetTempDir(), internal.BuildahExternalArtifactsDir, additionalBuildContext.Value)
if err != nil {
return errors.Wrapf(err, "unable to download context from external source %q", additionalBuildContext.Value)
}
// point context dir to the extracted path
contextDir = filepath.Join(path, subdir)
// populate cache for next RUN step
additionalBuildContext.DownloadedCache = contextDir
} else {
contextDir = additionalBuildContext.DownloadedCache
}
}
} else {
copy.From = additionalBuildContext.Value
}
}
if additionalBuildContext == nil {
if isStage, err := s.executor.waitForStage(s.ctx, from, s.stages[:s.index]); isStage && err != nil {
return err
}
@ -381,6 +427,15 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
} else {
return errors.Errorf("the stage %q has not been built", copy.From)
}
} else if additionalBuildContext.IsImage {
// Image was selected as additionalContext so only process image.
mountPoint, err := s.getImageRootfs(s.ctx, copy.From)
if err != nil {
return err
}
contextDir = mountPoint
}
// Original behaviour of buildah still stays true for COPY irrespective of additional context.
preserveOwnership = true
copyExcludes = excludes
} else {
@ -446,6 +501,55 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
if fromErr != nil {
return nil, errors.Wrapf(fromErr, "unable to resolve argument %q", kv[1])
}
// If additional buildContext contains this
// give priority to that and break if additional
// is not an external image.
if additionalBuildContext, ok := s.executor.additionalBuildContexts[from]; ok {
if additionalBuildContext.IsImage {
mountPoint, err := s.getImageRootfs(s.ctx, additionalBuildContext.Value)
if err != nil {
return nil, errors.Errorf("%s from=%s: image found with that name", flag, from)
}
// The `from` in stageMountPoints should point
// to `mountPoint` replaced from additional
// build-context. Reason: Parser will use this
// `from` to refer from stageMountPoints map later.
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint}
break
} else {
// Most likely this points to path on filesystem
// or external tar archive, Treat it as a stage
// nothing is different for this. So process and
// point mountPoint to path on host and it will
// be automatically handled correctly by since
// GetBindMount will honor IsStage:false while
// processing stageMountPoints.
mountPoint := additionalBuildContext.Value
if additionalBuildContext.IsURL {
// Check if following buildContext was already
// downloaded before in any other RUN step. If not
// download it and populate DownloadCache field for
// future RUN steps.
if additionalBuildContext.DownloadedCache == "" {
// additional context contains a tar file
// so download and explode tar to buildah
// temp and point context to that.
path, subdir, err := define.TempDirForURL(internalUtil.GetTempDir(), internal.BuildahExternalArtifactsDir, additionalBuildContext.Value)
if err != nil {
return nil, errors.Wrapf(err, "unable to download context from external source %q", additionalBuildContext.Value)
}
// point context dir to the extracted path
mountPoint = filepath.Join(path, subdir)
// populate cache for next RUN step
additionalBuildContext.DownloadedCache = mountPoint
} else {
mountPoint = additionalBuildContext.DownloadedCache
}
}
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: mountPoint}
break
}
}
// If the source's name corresponds to the
// result of an earlier stage, wait for that
// stage to finish being built.
@ -865,14 +969,14 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// squash the contents of the base image. Whichever is
// the case, we need to commit() to create a new image.
logCommit(s.output, -1)
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output); err != nil {
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output, s.executor.squash); err != nil {
return "", nil, errors.Wrapf(err, "error committing base container")
}
} else if len(s.executor.labels) > 0 || len(s.executor.annotations) > 0 {
// The image would be modified by the labels passed
// via the command line, so we need to commit.
logCommit(s.output, -1)
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output); err != nil {
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output, s.executor.squash); err != nil {
return "", nil, err
}
} else {
@ -923,6 +1027,25 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
if fromErr != nil {
return "", nil, errors.Wrapf(fromErr, "unable to resolve argument %q", arr[1])
}
// If additional buildContext contains this
// give priority to that and break if additional
// is not an external image.
if additionalBuildContext, ok := s.executor.additionalBuildContexts[from]; ok {
if !additionalBuildContext.IsImage {
// We don't need to pull this
// since this additional context
// is not an image.
break
} else {
// replace with image set in build context
from = additionalBuildContext.Value
if _, err := s.getImageRootfs(ctx, from); err != nil {
return "", nil, errors.Errorf("%s --from=%s: no stage or image found with that name", command, from)
}
break
}
}
// If the source's name corresponds to the
// result of an earlier stage, wait for that
// stage to finish being built.
@ -984,7 +1107,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// stage.
if lastStage || imageIsUsedLater {
logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), false, s.output)
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), false, s.output, s.executor.squash)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
@ -1018,7 +1141,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// we need to call ib.Run() to correctly put the args together before
// determining if a cached layer with the same build args already exists
// and that is done in the if block below.
if checkForLayers && step.Command != "arg" {
if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) {
cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step))
if err != nil {
return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build")
@ -1071,10 +1194,6 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
}
}
// We want to save history for other layers during a squashed build.
// Toggle flag allows executor to treat other instruction and layers
// as regular builds and only perform squashing at last
squashToggle := false
// Note: If the build has squash, we must try to re-use as many layers as possible if cache is found.
// So only perform commit if its the lastInstruction of lastStage.
if cacheID != "" {
@ -1091,30 +1210,27 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
}
}
} else {
if s.executor.squash {
// We want to save history for other layers during a squashed build.
// squashToggle flag allows executor to treat other instruction and layers
// as regular builds and only perform squashing at last
s.executor.squash = false
squashToggle = true
}
// We're not going to find any more cache hits, so we
// can stop looking for them.
checkForLayers = false
// Create a new image, maybe with a new layer, with the
// name for this stage if it's the last instruction.
logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName)
// While commiting we always set squash to false here
// because at this point we want to save history for
// layers even if its a squashed build so that they
// can be part of build-cache.
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, false)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
}
// Perform final squash for this build as we are one the,
// last instruction of last stage
if (s.executor.squash || squashToggle) && lastInstruction && lastStage {
s.executor.squash = true
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName)
// Create a squashed version of this image
// if we're supposed to create one and this
// is the last instruction of the last stage.
if s.executor.squash && lastInstruction && lastStage {
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, true)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing final squash step %+v", *step)
}
@ -1450,7 +1566,7 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p
// commit writes the container's contents to an image, using a passed-in tag as
// the name if there is one, generating a unique ID-based one otherwise.
// or commit via any custom exporter if specified.
func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) {
func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash bool) (string, reference.Canonical, error) {
ib := s.stage.Builder
var buildOutputOption define.BuildOutputOption
if s.executor.buildOutput != "" {
@ -1591,7 +1707,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
ReportWriter: writer,
PreferredManifestType: s.executor.outputFormat,
SystemContext: s.executor.systemContext,
Squash: s.executor.squash,
Squash: squash,
EmptyLayer: emptyLayer,
BlobDirectory: s.executor.blobDirectory,
SignBy: s.executor.signBy,

View File

@ -52,9 +52,9 @@ rpm-ostree install buildah
Note: [`podman`](https://podman.io) build is available by default.
### [Gentoo](https://www.gentoo.org)
[app-containers/podman](https://packages.gentoo.org/packages/app-containers/podman)
```bash
sudo emerge app-emulation/libpod
sudo emerge app-containers/podman
```
### [openSUSE](https://www.opensuse.org)

View File

@ -309,7 +309,7 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
// add subdirectory if specified
// cache parent directory
cacheParent := filepath.Join(getTempDir(), BuildahCacheDir)
cacheParent := filepath.Join(internalUtil.GetTempDir(), BuildahCacheDir)
// create cache on host if not present
err = os.MkdirAll(cacheParent, os.FileMode(0755))
if err != nil {
@ -597,12 +597,3 @@ func GetTmpfsMount(args []string) (specs.Mount, error) {
return newMount, nil
}
/* This is internal function and could be changed at any time */
/* for external usage please refer to buildah/pkg/parse.GetTempDir() */
func getTempDir() string {
if tmpdir, ok := os.LookupEnv("TMPDIR"); ok {
return tmpdir
}
return "/var/tmp"
}

View File

@ -1,5 +1,11 @@
package internal
const (
// Temp directory which stores external artifacts which are download for a build.
// Example: tar files from external sources.
BuildahExternalArtifactsDir = "buildah-external-artifacts"
)
// Types is internal packages are suspected to change with releases avoid using these outside of buildah
// StageMountDetails holds the Stage/Image mountpoint returned by StageExecutor

View File

@ -32,6 +32,14 @@ func LookupImage(ctx *types.SystemContext, store storage.Store, image string) (*
return localImage, nil
}
// GetTempDir returns base for a temporary directory on host.
func GetTempDir() string {
if tmpdir, ok := os.LookupEnv("TMPDIR"); ok {
return tmpdir
}
return "/var/tmp"
}
// ExportFromReader reads bytes from given reader and exports to external tar, directory or stdout.
func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
var err error

View File

@ -53,10 +53,12 @@ type BudResults struct {
Annotation []string
Authfile string
BuildArg []string
BuildContext []string
CacheFrom string
CertDir string
Compress bool
Creds string
CPPFlags []string
DisableCompression bool
DisableContentTrust bool
IgnoreFile string
@ -191,9 +193,11 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.StringArrayVar(&flags.Annotation, "annotation", []string{}, "set metadata for an image (default [])")
fs.StringVar(&flags.Authfile, "authfile", "", "path of the authentication file.")
fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
fs.StringArrayVar(&flags.BuildContext, "build-context", []string{}, "`argument=value` to supply additional build context to the builder")
fs.StringVar(&flags.CacheFrom, "cache-from", "", "images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.")
fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
fs.BoolVar(&flags.Compress, "compress", false, "this is a legacy option, which has no effect on the image")
fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)")
fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "this is a Docker specific option and is a NOOP")
@ -261,17 +265,19 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
// GetBudFlagsCompletions returns the FlagCompletions for the common build flags
func GetBudFlagsCompletions() commonComp.FlagCompletions {
flagCompletion := commonComp.FlagCompletions{}
flagCompletion["arch"] = commonComp.AutocompleteNone
flagCompletion["annotation"] = commonComp.AutocompleteNone
flagCompletion["arch"] = commonComp.AutocompleteNone
flagCompletion["authfile"] = commonComp.AutocompleteDefault
flagCompletion["build-arg"] = commonComp.AutocompleteNone
flagCompletion["build-context"] = commonComp.AutocompleteNone
flagCompletion["cache-from"] = commonComp.AutocompleteNone
flagCompletion["cert-dir"] = commonComp.AutocompleteDefault
flagCompletion["cpp-flag"] = commonComp.AutocompleteNone
flagCompletion["creds"] = commonComp.AutocompleteNone
flagCompletion["env"] = commonComp.AutocompleteNone
flagCompletion["file"] = commonComp.AutocompleteDefault
flagCompletion["from"] = commonComp.AutocompleteDefault
flagCompletion["format"] = commonComp.AutocompleteNone
flagCompletion["from"] = commonComp.AutocompleteDefault
flagCompletion["ignorefile"] = commonComp.AutocompleteDefault
flagCompletion["iidfile"] = commonComp.AutocompleteDefault
flagCompletion["jobs"] = commonComp.AutocompleteNone
@ -281,18 +287,18 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions {
flagCompletion["os"] = commonComp.AutocompleteNone
flagCompletion["os-feature"] = commonComp.AutocompleteNone
flagCompletion["os-version"] = commonComp.AutocompleteNone
flagCompletion["output"] = commonComp.AutocompleteNone
flagCompletion["pull"] = commonComp.AutocompleteDefault
flagCompletion["runtime-flag"] = commonComp.AutocompleteNone
flagCompletion["secret"] = commonComp.AutocompleteNone
flagCompletion["ssh"] = commonComp.AutocompleteNone
flagCompletion["sign-by"] = commonComp.AutocompleteNone
flagCompletion["signature-policy"] = commonComp.AutocompleteNone
flagCompletion["ssh"] = commonComp.AutocompleteNone
flagCompletion["tag"] = commonComp.AutocompleteNone
flagCompletion["target"] = commonComp.AutocompleteNone
flagCompletion["timestamp"] = commonComp.AutocompleteNone
flagCompletion["variant"] = commonComp.AutocompleteNone
flagCompletion["unsetenv"] = commonComp.AutocompleteNone
flagCompletion["output"] = commonComp.AutocompleteNone
flagCompletion["variant"] = commonComp.AutocompleteNone
return flagCompletion
}

View File

@ -175,6 +175,31 @@ func CommonBuildOptionsFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name
return commonOpts, nil
}
// GetAdditionalBuildContext consumes raw string and returns parsed AdditionalBuildContext
func GetAdditionalBuildContext(value string) (define.AdditionalBuildContext, error) {
ret := define.AdditionalBuildContext{IsURL: false, IsImage: false, Value: value}
if strings.HasPrefix(value, "docker-image://") {
ret.IsImage = true
ret.Value = strings.TrimPrefix(value, "docker-image://")
} else if strings.HasPrefix(value, "container-image://") {
ret.IsImage = true
ret.Value = strings.TrimPrefix(value, "container-image://")
} else if strings.HasPrefix(value, "docker://") {
ret.IsImage = true
ret.Value = strings.TrimPrefix(value, "docker://")
} else if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") {
ret.IsImage = false
ret.IsURL = true
} else {
path, err := filepath.Abs(value)
if err != nil {
return define.AdditionalBuildContext{}, errors.Wrapf(err, "unable to convert additional build-context %q path to absolute", value)
}
ret.Value = path
}
return ret, nil
}
func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOptions) error {
for _, opt := range securityOpts {
if opt == "no-new-privileges" {

View File

@ -2688,10 +2688,6 @@ func getSecretMount(tokens []string, secrets map[string]define.Secret, mountlabe
return nil, "", err
}
ctrFileOnHost = filepath.Join(containerWorkingDir, "secrets", id)
_, err = os.Stat(ctrFileOnHost)
if !os.IsNotExist(err) {
return nil, "", err
}
default:
return nil, "", errors.New("invalid source secret type")
}

View File

@ -18,7 +18,7 @@ require (
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible
github.com/moby/sys/mountinfo v0.6.1
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/runc v1.1.1
github.com/opencontainers/runc v1.1.2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/selinux v1.10.1
github.com/pkg/errors v0.9.1

View File

@ -521,8 +521,8 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.1.1 h1:PJ9DSs2sVwE0iVr++pAHE6QkS9tzcVWozlPifdwMgrU=
github.com/opencontainers/runc v1.1.1/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=

View File

@ -0,0 +1,84 @@
//go:build freebsd && cgo
// +build freebsd,cgo
package system
import (
"fmt"
"unsafe"
"golang.org/x/sys/unix"
)
// #include <unistd.h>
// #include <sys/vmmeter.h>
// #include <sys/sysctl.h>
// #include <vm/vm_param.h>
import "C"
func getMemInfo() (int64, int64, error) {
data, err := unix.SysctlRaw("vm.vmtotal")
if err != nil {
return -1, -1, fmt.Errorf("Can't get kernel info: %v", err)
}
if len(data) != C.sizeof_struct_vmtotal {
return -1, -1, fmt.Errorf("unexpected vmtotal size %d", len(data))
}
total := (*C.struct_vmtotal)(unsafe.Pointer(&data[0]))
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
npages := int64(C.sysconf(C._SC_PHYS_PAGES))
return pagesize * npages, pagesize * int64(total.t_free), nil
}
func getSwapInfo() (int64, int64, error) {
var (
total int64 = 0
used int64 = 0
)
swapCount, err := unix.SysctlUint32("vm.nswapdev")
if err != nil {
return -1, -1, fmt.Errorf("error reading vm.nswapdev: %v", err)
}
for i := 0; i < int(swapCount); i++ {
data, err := unix.SysctlRaw("vm.swap_info", i)
if err != nil {
return -1, -1, fmt.Errorf("error reading vm.swap_info.%d: %v", i, err)
}
if len(data) != C.sizeof_struct_xswdev {
return -1, -1, fmt.Errorf("unexpected swap_info size %d", len(data))
}
xsw := (*C.struct_xswdev)(unsafe.Pointer(&data[0]))
total += int64(xsw.xsw_nblks)
used += int64(xsw.xsw_used)
}
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
return pagesize * total, pagesize * (total - used), nil
}
// ReadMemInfo retrieves memory statistics of the host system and returns a
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
MemTotal, MemFree, err := getMemInfo()
if err != nil {
return nil, fmt.Errorf("error getting memory totals %v\n", err)
}
SwapTotal, SwapFree, err := getSwapInfo()
if err != nil {
return nil, fmt.Errorf("error getting swap totals %v\n", err)
}
if MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || SwapFree < 0 {
return nil, fmt.Errorf("error getting system memory info %v\n", err)
}
meminfo := &MemInfo{}
// Total memory is total physical memory less than memory locked by kernel
meminfo.MemTotal = MemTotal
meminfo.MemFree = MemFree
meminfo.SwapTotal = SwapTotal
meminfo.SwapFree = SwapFree
return meminfo, nil
}

View File

@ -1,4 +1,5 @@
// +build !linux,!windows,!solaris
//go:build !linux && !windows && !solaris && !freebsd
// +build !linux,!windows,!solaris,!freebsd
package system

View File

@ -1,4 +1,4 @@
#ifndef UNSHARE_NO_CODE_AT_ALL
#if !defined(UNSHARE_NO_CODE_AT_ALL) && defined(__linux__)
#define _GNU_SOURCE
#include <sys/types.h>

View File

@ -7,7 +7,7 @@ import (
"sync"
"github.com/pkg/errors"
"github.com/syndtr/gocapability/capability"
"github.com/sirupsen/logrus"
)
var (
@ -38,19 +38,13 @@ func HomeDir() (string, error) {
return homeDir, homeDirErr
}
// HasCapSysAdmin returns whether the current process has CAP_SYS_ADMIN.
func HasCapSysAdmin() (bool, error) {
hasCapSysAdminOnce.Do(func() {
currentCaps, err := capability.NewPid2(0)
func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname
if err != nil {
hasCapSysAdminErr = err
return
if format != "" {
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
} else {
logrus.Errorf("%v", err)
}
if err = currentCaps.Load(); err != nil {
hasCapSysAdminErr = err
return
os.Exit(1)
}
hasCapSysAdminRet = currentCaps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN)
})
return hasCapSysAdminRet, hasCapSysAdminErr
}

View File

@ -1,4 +1,5 @@
// +build linux,cgo,!gccgo
//go:build (linux && cgo && !gccgo) || (freebsd && cgo)
// +build linux,cgo,!gccgo freebsd,cgo
package unshare

View File

@ -0,0 +1,76 @@
#if !defined(UNSHARE_NO_CODE_AT_ALL) && defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static int _containers_unshare_parse_envint(const char *envname) {
char *p, *q;
long l;
p = getenv(envname);
if (p == NULL) {
return -1;
}
q = NULL;
l = strtol(p, &q, 10);
if ((q == NULL) || (*q != '\0')) {
fprintf(stderr, "Error parsing \"%s\"=\"%s\"!\n", envname, p);
_exit(1);
}
unsetenv(envname);
return l;
}
void _containers_unshare(void)
{
int pidfd, continuefd, n, pgrp, sid, ctty;
char buf[2048];
pidfd = _containers_unshare_parse_envint("_Containers-pid-pipe");
if (pidfd != -1) {
snprintf(buf, sizeof(buf), "%llu", (unsigned long long) getpid());
size_t size = write(pidfd, buf, strlen(buf));
if (size != strlen(buf)) {
fprintf(stderr, "Error writing PID to pipe on fd %d: %m\n", pidfd);
_exit(1);
}
close(pidfd);
}
continuefd = _containers_unshare_parse_envint("_Containers-continue-pipe");
if (continuefd != -1) {
n = read(continuefd, buf, sizeof(buf));
if (n > 0) {
fprintf(stderr, "Error: %.*s\n", n, buf);
_exit(1);
}
close(continuefd);
}
sid = _containers_unshare_parse_envint("_Containers-setsid");
if (sid == 1) {
if (setsid() == -1) {
fprintf(stderr, "Error during setsid: %m\n");
_exit(1);
}
}
pgrp = _containers_unshare_parse_envint("_Containers-setpgrp");
if (pgrp == 1) {
if (setpgrp(0, 0) == -1) {
fprintf(stderr, "Error during setpgrp: %m\n");
_exit(1);
}
}
ctty = _containers_unshare_parse_envint("_Containers-ctty");
if (ctty != -1) {
if (ioctl(ctty, TIOCSCTTY, 0) == -1) {
fprintf(stderr, "Error while setting controlling terminal to %d: %m\n", ctty);
_exit(1);
}
}
}
#endif

View File

@ -0,0 +1,179 @@
//go:build freebsd
// +build freebsd
package unshare
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"runtime"
"strconv"
"syscall"
"github.com/containers/storage/pkg/reexec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Cmd wraps an exec.Cmd created by the reexec package in unshare(),
// and one day might handle setting ID maps and other related setting*s
// by triggering initialization code in the child.
type Cmd struct {
*exec.Cmd
Setsid bool
Setpgrp bool
Ctty *os.File
Hook func(pid int) error
}
// Command creates a new Cmd which can be customized.
func Command(args ...string) *Cmd {
cmd := reexec.Command(args...)
return &Cmd{
Cmd: cmd,
}
}
func (c *Cmd) Start() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// Set environment variables to tell the child to synchronize its startup.
if c.Env == nil {
c.Env = os.Environ()
}
// Create the pipe for reading the child's PID.
pidRead, pidWrite, err := os.Pipe()
if err != nil {
return errors.Wrapf(err, "error creating pid pipe")
}
c.Env = append(c.Env, fmt.Sprintf("_Containers-pid-pipe=%d", len(c.ExtraFiles)+3))
c.ExtraFiles = append(c.ExtraFiles, pidWrite)
// Create the pipe for letting the child know to proceed.
continueRead, continueWrite, err := os.Pipe()
if err != nil {
pidRead.Close()
pidWrite.Close()
return errors.Wrapf(err, "error creating pid pipe")
}
c.Env = append(c.Env, fmt.Sprintf("_Containers-continue-pipe=%d", len(c.ExtraFiles)+3))
c.ExtraFiles = append(c.ExtraFiles, continueRead)
// Pass along other instructions.
if c.Setsid {
c.Env = append(c.Env, "_Containers-setsid=1")
}
if c.Setpgrp {
c.Env = append(c.Env, "_Containers-setpgrp=1")
}
if c.Ctty != nil {
c.Env = append(c.Env, fmt.Sprintf("_Containers-ctty=%d", len(c.ExtraFiles)+3))
c.ExtraFiles = append(c.ExtraFiles, c.Ctty)
}
// Make sure we clean up our pipes.
defer func() {
if pidRead != nil {
pidRead.Close()
}
if pidWrite != nil {
pidWrite.Close()
}
if continueRead != nil {
continueRead.Close()
}
if continueWrite != nil {
continueWrite.Close()
}
}()
// Start the new process.
err = c.Cmd.Start()
if err != nil {
return err
}
// Close the ends of the pipes that the parent doesn't need.
continueRead.Close()
continueRead = nil
pidWrite.Close()
pidWrite = nil
// Read the child's PID from the pipe.
pidString := ""
b := new(bytes.Buffer)
if _, err := io.Copy(b, pidRead); err != nil {
return errors.Wrapf(err, "Reading child PID")
}
pidString = b.String()
pid, err := strconv.Atoi(pidString)
if err != nil {
fmt.Fprintf(continueWrite, "error parsing PID %q: %v", pidString, err)
return errors.Wrapf(err, "error parsing PID %q", pidString)
}
// Run any additional setup that we want to do before the child starts running proper.
if c.Hook != nil {
if err = c.Hook(pid); err != nil {
fmt.Fprintf(continueWrite, "hook error: %v", err)
return err
}
}
return nil
}
func (c *Cmd) Run() error {
if err := c.Start(); err != nil {
return err
}
return c.Wait()
}
func (c *Cmd) CombinedOutput() ([]byte, error) {
return nil, errors.New("unshare: CombinedOutput() not implemented")
}
func (c *Cmd) Output() ([]byte, error) {
return nil, errors.New("unshare: Output() not implemented")
}
type Runnable interface {
Run() error
}
// ExecRunnable runs the specified unshare command, captures its exit status,
// and exits with the same status.
func ExecRunnable(cmd Runnable, cleanup func()) {
exit := func(status int) {
if cleanup != nil {
cleanup()
}
os.Exit(status)
}
if err := cmd.Run(); err != nil {
if exitError, ok := errors.Cause(err).(*exec.ExitError); ok {
if exitError.ProcessState.Exited() {
if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
if waitStatus.Exited() {
logrus.Debugf("%v", exitError)
exit(waitStatus.ExitStatus())
}
if waitStatus.Signaled() {
logrus.Debugf("%v", exitError)
exit(int(waitStatus.Signal()) + 128)
}
}
}
}
logrus.Errorf("%v", err)
logrus.Errorf("(Unable to determine exit status)")
exit(1)
}
exit(0)
}

View File

@ -414,17 +414,6 @@ type Runnable interface {
Run() error
}
func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname
if err != nil {
if format != "" {
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
} else {
logrus.Errorf("%v", err)
}
os.Exit(1)
}
}
// MaybeReexecUsingUserNamespace re-exec the process in a new namespace
func MaybeReexecUsingUserNamespace(evenForRoot bool) {
// If we've already been through this once, no need to try again.
@ -674,3 +663,20 @@ func ParseIDMappings(uidmap, gidmap []string) ([]idtools.IDMap, []idtools.IDMap,
}
return uid, gid, nil
}
// HasCapSysAdmin returns whether the current process has CAP_SYS_ADMIN.
func HasCapSysAdmin() (bool, error) {
hasCapSysAdminOnce.Do(func() {
currentCaps, err := capability.NewPid2(0)
if err != nil {
hasCapSysAdminErr = err
return
}
if err = currentCaps.Load(); err != nil {
hasCapSysAdminErr = err
return
}
hasCapSysAdminRet = currentCaps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN)
})
return hasCapSysAdminRet, hasCapSysAdminErr
}

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux
package unshare
@ -43,3 +44,8 @@ func GetHostIDMappings(pid string) ([]specs.LinuxIDMapping, []specs.LinuxIDMappi
func ParseIDMappings(uidmap, gidmap []string) ([]idtools.IDMap, []idtools.IDMap, error) {
return nil, nil, nil
}
// HasCapSysAdmin returns whether the current process has CAP_SYS_ADMIN.
func HasCapSysAdmin() (bool, error) {
return os.Geteuid() == 0, nil
}

View File

@ -1,4 +1,5 @@
// +build !linux,cgo
//go:build cgo && !(linux || freebsd)
// +build cgo,!linux,!freebsd
package unshare

View File

@ -173,6 +173,7 @@ type Store interface {
GraphRoot() string
GraphDriverName() string
GraphOptions() []string
PullOptions() map[string]string
UIDMap() []idtools.IDMap
GIDMap() []idtools.IDMap
@ -607,6 +608,7 @@ type store struct {
graphRoot string
graphDriverName string
graphOptions []string
pullOptions map[string]string
uidMap []idtools.IDMap
gidMap []idtools.IDMap
autoUsernsUser string
@ -726,6 +728,7 @@ func GetStore(options types.StoreOptions) (Store, error) {
additionalGIDs: nil,
usernsLock: usernsLock,
disableVolatile: options.DisableVolatile,
pullOptions: options.PullOptions,
}
if err := s.load(); err != nil {
return nil, err
@ -776,6 +779,14 @@ func (s *store) GraphOptions() []string {
return s.graphOptions
}
func (s *store) PullOptions() map[string]string {
cp := make(map[string]string, len(s.pullOptions))
for k, v := range s.pullOptions {
cp[k] = v
}
return cp
}
func (s *store) UIDMap() []idtools.IDMap {
return copyIDMap(s.uidMap)
}

View File

@ -187,6 +187,7 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti
return opts, err
}
opts.RunRoot = rootlessRuntime
opts.PullOptions = systemOpts.PullOptions
if systemOpts.RootlessStoragePath != "" {
opts.GraphRoot, err = expandEnvPath(systemOpts.RootlessStoragePath, rootlessUID)
if err != nil {
@ -203,7 +204,7 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti
opts.GraphDriverName = driver
}
if opts.GraphDriverName == overlay2 {
logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver.")
logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver")
opts.GraphDriverName = overlayDriver
}
@ -280,7 +281,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
if err == nil {
keys := meta.Undecoded()
if len(keys) > 0 {
logrus.Warningf("Failed to decode the keys %q from %q.", keys, configFile)
logrus.Warningf("Failed to decode the keys %q from %q", keys, configFile)
}
} else {
if !os.IsNotExist(err) {
@ -299,11 +300,11 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
storeOptions.GraphDriverName = config.Storage.Driver
}
if storeOptions.GraphDriverName == overlay2 {
logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver.")
logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver")
storeOptions.GraphDriverName = overlayDriver
}
if storeOptions.GraphDriverName == "" {
logrus.Errorf("The storage 'driver' option must be set in %s, guarantee proper operation.", configFile)
logrus.Errorf("The storage 'driver' option must be set in %s to guarantee proper operation", configFile)
}
if config.Storage.RunRoot != "" {
storeOptions.RunRoot = config.Storage.RunRoot

6
vendor/modules.txt vendored
View File

@ -87,7 +87,7 @@ github.com/containernetworking/cni/pkg/version
# github.com/containernetworking/plugins v1.1.1
## explicit
github.com/containernetworking/plugins/pkg/ns
# github.com/containers/buildah v1.26.1
# github.com/containers/buildah v1.26.1-0.20220524184833-5500333c2e06
## explicit
github.com/containers/buildah
github.com/containers/buildah/bind
@ -156,7 +156,7 @@ github.com/containers/common/version
# github.com/containers/conmon v2.0.20+incompatible
## explicit
github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059
# github.com/containers/image/v5 v5.21.2-0.20220520105616-e594853d6471
## explicit
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
@ -236,7 +236,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
# github.com/containers/storage v1.41.1-0.20220511210719-cacc3325a9c8
# github.com/containers/storage v1.41.1-0.20220517121726-5019cd55275c
## explicit
github.com/containers/storage
github.com/containers/storage/drivers