Compare commits

...

61 Commits
v0.10 ... v0.12

Author SHA1 Message Date
Daniel J Walsh
e073df11aa Merge pull request #473 from rhatdan/master
Bump version to 0.12
2018-02-12 12:08:24 -05:00
Daniel J Walsh
8badcc2d02 Bump version to 0.12
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2018-02-12 11:48:48 -05:00
Daniel J Walsh
a586779353 We are copying a directory not a single file
When populating a container from a container image with a
volume directory, we need to copy the content of the source
directory into the target.  The code was mistakenly looking
for a file not a directory.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #471
Approved by: nalind
2018-02-12 15:57:23 +00:00
umohnani8
4eb654f10c Removing docs and completions for run options
Figured that these options need to be in from and bud instead.
Removed the options from the documentation of run and bud for now.

Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #470
Approved by: rhatdan
2018-02-12 15:22:09 +00:00
Boaz Shuster
f29314579d Return multi errors in buildah-rm
Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #458
Approved by: rhatdan
2018-02-12 12:02:37 +00:00
TomSweeneyRedHat
9e20c3d948 Don't drop error on mix case imagename
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #468
Approved by: rhatdan
2018-02-11 11:41:44 +00:00
TomSweeneyRedHat
46c1a54b15 Revert to using latest go-md2man
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #463
Approved by: rhatdan
2018-02-10 12:24:54 +00:00
Benjamin Kircher
67b565da7b Docs: note that buildah needs to run as root
You have to be root to run buildah. This commit adds a notice to the
buildah(1) man-page and improves the front-page README.md a bit so that
this is more obvious to the user.

Fixes issue #420.

Signed-off-by: Benjamin Kircher <benjamin.kircher@gmail.com>

Closes: #462
Approved by: rhatdan
2018-02-10 12:07:07 +00:00
baude
dd4a6aea97 COPR enablement
For COPR builds, we will use a a slightly modified spec and the
makesrpm method over SCM builds so we can have dynamic package
names.

Signed-off-by: baude <bbaude@redhat.com>

Closes: #460
Approved by: rhatdan
2018-02-10 11:49:46 +00:00
William Henry
9116598a2e Added handing for simpler error message for Unknown Dockerfile instructions.
Signed-off-by: William Henry <whenry@redhat.com>

Closes: #457
Approved by: rhatdan
2018-02-10 11:32:38 +00:00
TomSweeneyRedHat
df2a10d43f Add limitation to buildah rmi man page
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #464
Approved by: rhatdan
2018-02-10 11:15:34 +00:00
TomSweeneyRedHat
e9915937ac Rename tutorials.md to README.md in tutorial dir
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #459
Approved by: rhatdan
2018-02-10 11:14:52 +00:00
Daniel J Walsh
5a9c591abf Merge pull request #455 from umohnani8/certs
Make /etc/containers/certs.d the default certs directory
2018-02-07 08:44:32 -05:00
Daniel J Walsh
531ef9159d Merge pull request #446 from umohnani8/flags_docs
Add documentation and completions for the following flags
2018-02-06 17:21:41 -05:00
umohnani8
811cf927d7 Change default certs directory to /etc/containers/certs.dir
Made changes to the man pages to reflect this.

Signed-off-by: umohnani8 <umohnani@redhat.com>
2018-02-06 17:04:34 -05:00
umohnani8
032b56ee8d Vendor in latest containers/image
Adds support for default certs directory to be /etc/containers/certs.d

Signed-off-by: umohnani8 <umohnani@redhat.com>
2018-02-06 17:04:34 -05:00
Daniel J Walsh
6af847dd2a Vendor in latest containers/storage
A patch got merged into containers/storage that makes sure SELinux labels
are appliced when committing to storage.  This prevents a failure condition
which arrises from leaked mount points between the time a container is mounted
and it is committed.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #453
Approved by: TomSweeneyRedHat
2018-02-06 20:26:45 +00:00
Nalin Dahyabhai
6b207f7b0c Fix unintended reversal of the ignoreUnrecognizedInstructions flag
We were interpreting the ignoreUnrecognizedInstructions incorrectly, so
fix that, and call out the unrecognized instruction keyword in the error
message (or debug message, if we're ignoring it).

Should fix #451.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #452
Approved by: rhatdan
2018-02-06 18:58:25 +00:00
umohnani8
c14697ebe4 Add documentation and completions for the following flags
--add-host
	--cgroup-parent
	--cpu-period
	--cpu-quota
	--cpu-shares
	--cpuset-mems
	--memory
	--memory-swap
	--security-opt
	--ulimit

These flags are going to be used by buildah run and bud.
The implementation will follow in another PR.

Signed-off-by: umohnani8 <umohnani@redhat.com>
2018-02-06 12:45:17 -05:00
Nalin Dahyabhai
c35493248e build-using-dockerfile: set the 'author' field for MAINTAINER
When we encounter the MAINTAINER keyword in a Dockerfile, imagebuilder
updates the Author field in the imagebuilder.Builder structure.  Pick up
that value when we go to commit the image.

Should fix #448.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #450
Approved by: rhatdan
2018-02-06 01:18:55 +00:00
umohnani8
d03a894969 Fix md2man issues
Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #447
Approved by: rhatdan
2018-02-05 21:52:41 +00:00
Boaz Shuster
fbb8b702bc Return exit code 1 when buildah-rmi fails
Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #412
Approved by: rhatdan
2018-02-05 13:50:30 +00:00
Boaz Shuster
815cedfc71 Trim the image reference to just its name before calling getImageName
When setting a container name the getImageName function goes through
all the names of the resolved image and finds the name that contains
the given name by the user.

However, if the user is specifying "docker.io/tagged-image"
the Docker transport returns "docker.io/library/tagged-image" which
makes getImageName returns the original image name because it does
not find a match.

To resolve this issue before calling getImageName the image given
by the user will be trimmed to be just the name.

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #422
Approved by: rhatdan
2018-02-04 11:26:43 +00:00
TomSweeneyRedHat
1c97f6ac2c Bump Fedora 26 to 27 in rpm test
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #444
Approved by: rhatdan
2018-02-04 11:25:25 +00:00
umohnani8
bc9d574c10 Add new line after executing template for buildah inspect
No new line was returned when using the --format flag for buildah
inspect.

Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #442
Approved by: rhatdan
2018-02-02 21:11:52 +00:00
TomSweeneyRedHat
c84db980ae Touch up rmi -f usage statement
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #441
Approved by: rhatdan
2018-02-02 20:15:08 +00:00
umohnani8
85a37b39e8 Add --format and --filter to buildah containers
buildah containers now supports oretty-printing using a Go template
with the --format flag. And output can be filtered based on id, name, or
ancestor.

Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #437
Approved by: rhatdan
2018-02-02 19:32:06 +00:00
Arthur Mello
49095a83f8 Add --prune,-p option to rmi command
Allows rmi to remove all dangling images (images without a tag and without a child image)
Add new test case

Signed-off-by: Arthur Mello <amello@redhat.com>

Closes: #418
Approved by: rhatdan
2018-02-01 10:50:33 +00:00
TomSweeneyRedHat
6c05a352df Add authfile param to commit
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #433
Approved by: rhatdan
2018-02-01 05:48:09 +00:00
umohnani8
1849466827 Fix --runtime-flag for buildah run and bud
The --runtime-flag flag for buildah run and bud would fail
whenever the global flags of the runtime were passed to it.
Changed it to accept the format [global-flag]=[value] where
global-flag would be converted to --[global-flag] in the code.

Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #431
Approved by: rhatdan
2018-01-30 18:21:46 +00:00
umohnani8
4f38267342 format should override quiet for images
quiet was overriding format, but we want format to override quiet
if both the flags are set for buildah images.

Changed it so that it errors out if both quiet and format are set.

Signed-off-by: umohnani8 <umohnani@redhat.com>

Closes: #426
Approved by: rhatdan
2018-01-30 17:06:51 +00:00
TomSweeneyRedHat
9790b89771 Allow all auth params to work with bud
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #419
Approved by: rhatdan
2018-01-30 15:41:52 +00:00
Fabio Bertinatto
61f5319504 Don't overwrite directory permissions on --chown
Signed-off-by: Fabio Bertinatto <fbertina@redhat.com>

Closes: #389
Approved by: rhatdan
2018-01-30 05:09:06 +00:00
Boaz Shuster
947714fbd2 Unescape HTML characters output into the terminal
By default, the JSON encoder from the Go standard library
escapes the HTML characters which causes the maintainer output
looks strange:

"maintainer": "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e"
Instead of:
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"

This patch fixes this issue in "buildah-inspect" only as this is
the only place that such characters are displayed.

Note: if the output of "buildah-inspect" is piped or redirected
then the HTML characters are not escaped.

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #421
Approved by: rhatdan
2018-01-30 04:51:17 +00:00
Boaz Shuster
c615c3e23d Fix typo then->than
Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #423
Approved by: rhatdan
2018-01-29 05:50:24 +00:00
Daniel J Walsh
d0e1ad1a1a Merge pull request #415 from TomSweeneyRedHat/dev/tsweeney/pwdprompt
Prompt for un/pwd if not supplied with --creds
2018-01-27 09:34:51 +01:00
Boaz Shuster
b68f88c53d Fix: setting the container name to the image
In commit 47ac96155f the image name that is used
for setting the container name is taken from the resolved image
unless it is empty.

The image has the "Names" field and right now the first name is
taken. However, when the image is a tagged image, the container name
will end up using the original name instead of the given one.

For example:

$ buildah tag busybox busybox1
$ buildah from busybox1

Will set the name of the container as "busybox-working-container"
while it was expected to be "busybox1-working-container".

This patch fixes this particular issue.

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #399
Approved by: rhatdan
2018-01-26 08:07:58 +00:00
TomSweeneyRedHat
7dc787a9c7 Prompt for un/pwd if not supplied with --creds
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
2018-01-25 15:03:59 -05:00
TomSweeneyRedHat
2dbb2a13ed Make bud be really quiet
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #408
Approved by: rhatdan
2018-01-24 15:11:30 +00:00
Boaz Shuster
ad49b24d0b Return a better error message when failed to resolve an image
During the creation of a new builder object there are errors
that are only logged into "logrus.Debugf".

If in the end of the process "ref" or "img" are nil and "options.FromImage"
is set then it means that there was an issue.
By default, it was assumed that the image name is wrong. Yet,
this assumption isn't always correct. For example, it might fail due to
authorization or connection errors.

In this patch, I am attempting to fix this problem by checking the
last error stored in the "err" variable and returning the cause
of the failure.

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #406
Approved by: rhatdan
2018-01-24 14:03:28 +00:00
Boaz Shuster
ba128004ca Fix "make validate" warnings
Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #405
Approved by: rhatdan
2018-01-22 14:46:54 +00:00
TomSweeneyRedHat
5179733c63 Update auth tests and fix bud man page
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #404
Approved by: rhatdan
2018-01-22 13:34:50 +00:00
TomSweeneyRedHat
40c3a57d5a Try to fix buildah-containers.md CI test issue
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #395
Approved by: rhatdan
2018-01-19 20:20:13 +00:00
Daniel J Walsh
de9e71dda7 Drop support for 1.7
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #400
Approved by: rhatdan
2018-01-18 23:00:15 +00:00
TomSweeneyRedHat
1052f3ba40 Create Buildah issue template
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #397
Approved by: rhatdan
2018-01-18 11:56:31 +00:00
Daniel J Walsh
6bad262ff1 Bump to version 0.11
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #394
Approved by: @ripcurld0
2018-01-17 13:42:19 +00:00
TomSweeneyRedHat
092591620b Show ctrid when doing rm -all
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #392
Approved by: rhatdan
2018-01-16 18:38:28 +00:00
Boaz Shuster
4d6c90e902 vendor containers/image to 386d6c33c9d622ed84baf14f4b1ff1be86800ccd
Signed-off-by: Boaz Shuster <bshuster@redhat.com>

Closes: #393
Approved by: rhatdan
2018-01-16 16:44:34 +00:00
TomSweeneyRedHat
17d9a73329 Touchup rm and container man pages
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #391
Approved by: rhatdan
2018-01-16 15:48:29 +00:00
Boaz Shuster
fe2de4f491 Handle commit error gracefully
This change gives a better error message when the commit fails
because of bad authentication.

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>

Closes: #385
Approved by: rhatdan
2018-01-15 15:48:20 +00:00
TomSweeneyRedHat
adfb256a0f Remove new errors and use established ones in rmi
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #388
Approved by: rhatdan
2018-01-10 17:01:41 +00:00
Huamin Chen
029bdbcbd0 fix buildah push description
Signed-off-by: Huamin Chen <hchen@redhat.com>

Closes: #387
Approved by: rhatdan
2018-01-09 18:45:53 +00:00
TomSweeneyRedHat
fd995e6166 Add --all functionality to rmi
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #384
Approved by: rhatdan
2018-01-08 21:07:25 +00:00
Nalin Dahyabhai
ae7d2f3547 Ignore sequential duplicate layers when reading v2s1
When a v2s1 image is stored to disk, some of the layer blobs listed in
its manifest may be discarded as.  Account for this.

Start treating a failure to decode v1compat information as a fatal error
instead of trying to fake it.

Tweak how we build the created-by field in history when generating one
from v2s1 information to better match what we see in v2s2 images.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #383
Approved by: rhatdan
2018-01-08 21:06:35 +00:00
Nalin Dahyabhai
86fa0803e8 Sanity check the history/diffid list sizes
When building an image's config blob, add a sanity check that the number
of diffIDs that we're including matches the number of entries in the
history which don't claim to be empty layers.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #383
Approved by: rhatdan
2018-01-08 21:06:35 +00:00
Nalin Dahyabhai
81dfe0a964 When we say we skip a secrets config file, do so
When we warn about not processing a secrets configuration file, actually
skip anything we might have salvaged from it to make our behavior match
the warning.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #380
Approved by: rhatdan
2018-01-05 16:09:53 +00:00
Nalin Dahyabhai
9bff989832 Use NewImageSource() instead of NewImage()
Use NewImageSource() instead of NewImage() when checking if an image is
actually there, since it makes the image library do less work while
answering the same question for us.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #381
Approved by: rhatdan
2018-01-05 15:54:34 +00:00
TomSweeneyRedHat
b8740e386e Add --all to remove containers
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #382
Approved by: rhatdan
2018-01-05 15:53:19 +00:00
Daniel J Walsh
9f5e1b3a77 Make lint was complaining about some vetshowed err
We often use err as a variable inside of subblocks, and
we don't want golint to complain about it.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #379
Approved by: nalind
2018-01-03 21:10:28 +00:00
Daniel J Walsh
01f8c7afee Remove chrootuser handling and use libpod/pkg
I have made a subpackage of libpod to handle chrootuser,
using the user code from buildah.

This patch removes user handling from buildah and uses
projectatomic/libpod/pkg/chrootuser

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #377
Approved by: nalind
2018-01-03 15:36:10 +00:00
TomSweeneyRedHat
67e5341846 Add kernel version requirement to install.md and touchups
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #372
Approved by: rhatdan
2018-01-03 13:00:36 +00:00
93 changed files with 3876 additions and 749 deletions

28
.copr/Makefile Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/make -f
spec := contrib/rpm/buildah_copr.spec
outdir := $(CURDIR)
tmpdir := build
gitdir := $(PWD)/.git
rev := $(shell sed 's/\(.......\).*/\1/' $(gitdir)/$$(sed -n '/^ref:/{s/.* //;p}' $(gitdir)/HEAD))
date := $(shell date +%Y%m%d.%H%M)
version := $(shell sed -n '/Version:/{s/.* //;p}' $(spec))
release := $(date).git.$(rev)
srpm: $(outdir)/buildah-$(version)-$(release).src.rpm
$(tmpdir)/buildah.spec: $(spec)
@mkdir -p $(tmpdir)
sed '/^Release:/s/\(: *\).*/\1$(release)%{?dist}/' $< >$@
$(tmpdir)/$(version).tar.gz: $(gitdir)/..
@mkdir -p $(tmpdir)
tar c --exclude-vcs --exclude-vcs-ignores -C $< --transform 's|^\.|buildah-$(version)|' . | gzip -9 >$@
$(outdir)/buildah-$(version)-$(release).src.rpm: $(tmpdir)/buildah.spec $(tmpdir)/$(version).tar.gz
@mkdir -p $(outdir)
rpmbuild -D'_srcrpmdir $(outdir)' -D'_sourcedir $(tmpdir)' -bs $(tmpdir)/buildah.spec
.PHONY: srpm

65
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,65 @@
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below. If you fail to provide this
information within 7 days, we cannot debug your issue and will close it. We
will, however, reopen it if you later provide the information.
---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->
**Description**
<!--
Briefly describe the problem you are having in a few paragraphs.
-->
**Steps to reproduce the issue:**
1.
2.
3.
**Describe the results you received:**
**Describe the results you expected:**
**Output of `rpm -q buildah` or `apt list buildah`:**
```
(paste your output here)
```
**Output of `buildah version`:**
```
(paste your output here)
```
**Output of `cat /etc/*release`:**
```
(paste your output here)
```
**Output of `uname -a`:**
```
(paste your output here)
```
**Output of `cat /etc/containers/storage.conf`:**
```
(paste your output here)
```

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
docs/buildah*.1
/buildah
/imgtype
/build/

View File

@@ -2,7 +2,6 @@ language: go
dist: trusty
sudo: required
go:
- 1.7
- 1.8
- 1.9.x
- tip

View File

@@ -24,7 +24,7 @@ imgtype: *.go docker/*.go util/*.go tests/imgtype.go
.PHONY: clean
clean:
$(RM) buildah imgtype
$(RM) buildah imgtype build
$(MAKE) -C docs clean
.PHONY: docs

View File

@@ -23,18 +23,12 @@ The Buildah package provides a command line tool which can be used to
**[Tutorials](docs/tutorials/tutorials.md)**
## runc Requirement
Buildah uses `runc` to run commands when `buildah run` is used, or when `buildah build-using-dockerfile`
encounters a `RUN` instruction, so you'll also need to build and install a compatible version of
[runc](https://github.com/opencontainers/runc) for Buildah to call for those cases.
## Example
From [`./examples/lighttpd.sh`](examples/lighttpd.sh):
```bash
cat > lighttpd.sh <<EOF
$ cat > lighttpd.sh <<EOF
#!/bin/bash -x
ctr1=`buildah from ${1:-fedora}`
@@ -54,8 +48,8 @@ buildah config $ctr1 --port 80
buildah commit $ctr1 ${2:-$USER/lighttpd}
EOF
chmod +x lighttpd.sh
./lighttpd.sh
$ chmod +x lighttpd.sh
$ sudo ./lighttpd.sh
```
## Commands
@@ -71,7 +65,7 @@ chmod +x lighttpd.sh
| [buildah-images(1)](/docs/buildah-images.md) | List images in local storage. |
| [buildah-inspect(1)](/docs/buildah-inspect.md) | Inspects the configuration of a container or image. |
| [buildah-mount(1)](/docs/buildah-mount.md) | Mount the working container's root filesystem. |
| [buildah-push(1)](/docs/buildah-push.md) | Copies an image from local storage. |
| [buildah-push(1)](/docs/buildah-push.md) | Push an image from local storage to elsewhere. |
| [buildah-rm(1)](/docs/buildah-rm.md) | Removes one or more working containers. |
| [buildah-rmi(1)](/docs/buildah-rmi.md) | Removes one or more images. |
| [buildah-run(1)](/docs/buildah-run.md) | Run a command inside of the container. |

100
add.go
View File

@@ -14,6 +14,7 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/pkg/chrootuser"
"github.com/sirupsen/logrus"
)
@@ -74,12 +75,17 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
logrus.Errorf("error unmounting container: %v", err2)
}
}()
// Find out which user (and group) the destination should belong to.
user, err := b.user(mountPoint, options.Chown)
if err != nil {
return err
}
dest := mountPoint
if destination != "" && filepath.IsAbs(destination) {
dest = filepath.Join(dest, destination)
} else {
if err = os.MkdirAll(filepath.Join(dest, b.WorkDir()), 0755); err != nil {
return errors.Wrapf(err, "error ensuring directory %q exists)", filepath.Join(dest, b.WorkDir()))
if err = ensureDir(filepath.Join(dest, b.WorkDir()), user, 0755); err != nil {
return err
}
dest = filepath.Join(dest, b.WorkDir(), destination)
}
@@ -87,8 +93,8 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
// with a '/', create it so that we can be sure that it's a directory,
// and any files we're copying will be placed in the directory.
if len(destination) > 0 && destination[len(destination)-1] == os.PathSeparator {
if err = os.MkdirAll(dest, 0755); err != nil {
return errors.Wrapf(err, "error ensuring directory %q exists", dest)
if err = ensureDir(dest, user, 0755); err != nil {
return err
}
}
// Make sure the destination's parent directory is usable.
@@ -106,11 +112,6 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
return errors.Errorf("destination %q is not a directory", dest)
}
// Find out which user (and group) the destination should belong to.
user, err := b.user(mountPoint, options)
if err != nil {
return err
}
for _, src := range source {
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
// We assume that source is a file, and we're copying
@@ -129,7 +130,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
if err := addURL(d, src); err != nil {
return err
}
if err := setOwner(d, user); err != nil {
if err := setOwner("", d, user); err != nil {
return err
}
continue
@@ -152,15 +153,14 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
// the source directory into the target directory. Try
// to create it first, so that if there's a problem,
// we'll discover why that won't work.
d := dest
if err := os.MkdirAll(d, 0755); err != nil {
return errors.Wrapf(err, "error ensuring directory %q exists", d)
if err = ensureDir(dest, user, 0755); err != nil {
return err
}
logrus.Debugf("copying %q to %q", gsrc+string(os.PathSeparator)+"*", d+string(os.PathSeparator)+"*")
if err := copyWithTar(gsrc, d); err != nil {
return errors.Wrapf(err, "error copying %q to %q", gsrc, d)
logrus.Debugf("copying %q to %q", gsrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
if err := copyWithTar(gsrc, dest); err != nil {
return errors.Wrapf(err, "error copying %q to %q", gsrc, dest)
}
if err := setOwner(d, user); err != nil {
if err := setOwner(gsrc, dest, user); err != nil {
return err
}
continue
@@ -178,7 +178,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
if err := copyFileWithTar(gsrc, d); err != nil {
return errors.Wrapf(err, "error copying %q to %q", gsrc, d)
}
if err := setOwner(d, user); err != nil {
if err := setOwner(gsrc, d, user); err != nil {
return err
}
continue
@@ -194,34 +194,60 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
}
// user returns the user (and group) information which the destination should belong to.
func (b *Builder) user(mountPoint string, options AddAndCopyOptions) (specs.User, error) {
if options.Chown != "" {
return getUser(mountPoint, options.Chown)
func (b *Builder) user(mountPoint string, userspec string) (specs.User, error) {
if userspec == "" {
userspec = b.User()
}
return getUser(mountPoint, b.User())
uid, gid, err := chrootuser.GetUser(mountPoint, userspec)
u := specs.User{
UID: uid,
GID: gid,
Username: userspec,
}
return u, err
}
// setOwner sets the uid and gid owners of a given path.
// If path is a directory, recursively changes the owner.
func setOwner(path string, user specs.User) error {
fi, err := os.Stat(path)
func setOwner(src, dest string, user specs.User) error {
fid, err := os.Stat(dest)
if err != nil {
return errors.Wrapf(err, "error reading %q", path)
return errors.Wrapf(err, "error reading %q", dest)
}
if fi.IsDir() {
err2 := filepath.Walk(path, func(p string, info os.FileInfo, we error) error {
if err3 := os.Lchown(p, int(user.UID), int(user.GID)); err3 != nil {
return errors.Wrapf(err3, "error setting ownership of %q", p)
}
return nil
})
if err2 != nil {
return errors.Wrapf(err2, "error walking dir %q to set ownership", path)
if !fid.IsDir() || src == "" {
if err := os.Lchown(dest, int(user.UID), int(user.GID)); err != nil {
return errors.Wrapf(err, "error setting ownership of %q", dest)
}
return nil
}
if err := os.Lchown(path, int(user.UID), int(user.GID)); err != nil {
return errors.Wrapf(err, "error setting ownership of %q", path)
err = filepath.Walk(src, func(p string, info os.FileInfo, we error) error {
relPath, err2 := filepath.Rel(src, p)
if err2 != nil {
return errors.Wrapf(err2, "error getting relative path of %q to set ownership on destination", p)
}
if relPath != "." {
absPath := filepath.Join(dest, relPath)
if err2 := os.Lchown(absPath, int(user.UID), int(user.GID)); err != nil {
return errors.Wrapf(err2, "error setting ownership of %q", absPath)
}
}
return nil
})
if err != nil {
return errors.Wrapf(err, "error walking dir %q to set ownership", src)
}
return nil
}
// ensureDir creates a directory if it doesn't exist, setting ownership and permissions as passed by user and perm.
func ensureDir(path string, user specs.User, perm os.FileMode) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.MkdirAll(path, perm); err != nil {
return errors.Wrapf(err, "error ensuring directory %q exists", path)
}
if err := os.Chown(path, int(user.UID), int(user.GID)); err != nil {
return errors.Wrapf(err, "error setting ownership of %q", path)
}
}
return nil
}

View File

@@ -20,7 +20,7 @@ const (
// identify working containers.
Package = "buildah"
// Version for the Package
Version = "0.10"
Version = "0.12"
// The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to

View File

@@ -47,7 +47,7 @@ func addAndCopyCmd(c *cli.Context, extractLocalArchives bool) error {
return err
}
// If list is greater then one, the last item is the destination
// If list is greater than one, the last item is the destination
dest := ""
size := len(args)
if size > 1 {

View File

@@ -21,6 +21,16 @@ var (
Name: "build-arg",
Usage: "`argument=value` to supply to the builder",
},
cli.StringFlag{
Name: "cert-dir",
Value: "",
Usage: "use certificates at the specified path to access the registry",
},
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "use `[username[:password]]` for accessing the registry",
},
cli.StringSliceFlag{
Name: "file, f",
Usage: "`pathname or URL` of a Dockerfile",
@@ -181,20 +191,29 @@ func budCmd(c *cli.Context) error {
return err
}
systemContext, err := systemContextFromOptions(c)
if err != nil {
return errors.Wrapf(err, "error building system context")
}
runtimeFlags := []string{}
for _, arg := range c.StringSlice("runtime-flag") {
runtimeFlags = append(runtimeFlags, "--"+arg)
}
options := imagebuildah.BuildOptions{
ContextDirectory: contextDir,
PullPolicy: pullPolicy,
Compression: imagebuildah.Gzip,
Quiet: c.Bool("quiet"),
SignaturePolicyPath: c.String("signature-policy"),
SkipTLSVerify: !c.Bool("tls-verify"),
Args: args,
Output: output,
AdditionalTags: tags,
Runtime: c.String("runtime"),
RuntimeArgs: c.StringSlice("runtime-flag"),
RuntimeArgs: runtimeFlags,
OutputFormat: format,
AuthFilePath: c.String("authfile"),
SystemContext: systemContext,
}
if !c.Bool("quiet") {
options.ReportWriter = os.Stderr

View File

@@ -10,11 +10,16 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
"github.com/projectatomic/buildah"
"github.com/projectatomic/buildah/util"
"github.com/urfave/cli"
)
var (
commitFlags = []cli.Flag{
cli.StringFlag{
Name: "authfile",
Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
},
cli.StringFlag{
Name: "cert-dir",
Value: "",
@@ -23,7 +28,7 @@ var (
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "use `username[:password]` for accessing the registry",
Usage: "use `[username[:password]]` for accessing the registry",
},
cli.BoolFlag{
Name: "disable-compression, D",
@@ -143,7 +148,10 @@ func commitCmd(c *cli.Context) error {
}
err = builder.Commit(dest, options)
if err != nil {
return errors.Wrapf(err, "error committing container %q to %q", builder.Container, image)
return util.GetFailureCause(
err,
errors.Wrapf(err, "error committing container %q to %q", builder.Container, image),
)
}
if c.Bool("rm") {

View File

@@ -1,11 +1,11 @@
package main
import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"syscall"
"time"
is "github.com/containers/image/storage"
@@ -15,6 +15,7 @@ import (
"github.com/pkg/errors"
"github.com/projectatomic/buildah"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
var needToShutdownStore = false
@@ -146,25 +147,35 @@ func systemContextFromOptions(c *cli.Context) (*types.SystemContext, error) {
return ctx, nil
}
func parseCreds(creds string) (string, string, error) {
func parseCreds(creds string) (string, string) {
if creds == "" {
return "", "", errors.Wrapf(syscall.EINVAL, "credentials can't be empty")
return "", ""
}
up := strings.SplitN(creds, ":", 2)
if len(up) == 1 {
return up[0], "", nil
return up[0], ""
}
if up[0] == "" {
return "", "", errors.Wrapf(syscall.EINVAL, "username can't be empty")
return "", up[1]
}
return up[0], up[1], nil
return up[0], up[1]
}
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
username, password, err := parseCreds(creds)
if err != nil {
return nil, err
username, password := parseCreds(creds)
if username == "" {
fmt.Print("Username: ")
fmt.Scanln(&username)
}
if password == "" {
fmt.Print("Password: ")
termPassword, err := terminal.ReadPassword(0)
if err != nil {
return nil, errors.Wrapf(err, "could not read password from terminal")
}
password = string(termPassword)
}
return &types.DockerAuthConfig{
Username: username,
Password: password,

View File

@@ -3,7 +3,11 @@ package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"text/template"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/projectatomic/buildah"
"github.com/urfave/cli"
@@ -17,12 +21,43 @@ type jsonContainer struct {
ContainerName string `json:"containername"`
}
type containerOutputParams struct {
ContainerID string
Builder string
ImageID string
ImageName string
ContainerName string
}
type containerOptions struct {
all bool
format string
json bool
noHeading bool
noTruncate bool
quiet bool
}
type containerFilterParams struct {
id string
name string
ancestor string
}
var (
containersFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "also list non-buildah containers",
},
cli.StringFlag{
Name: "filter, f",
Usage: "filter output based on conditions provided",
},
cli.StringFlag{
Name: "format",
Usage: "pretty-print containers using a Go template",
},
cli.BoolFlag{
Name: "json",
Usage: "output in JSON format",
@@ -60,38 +95,35 @@ func containersCmd(c *cli.Context) error {
return err
}
quiet := c.Bool("quiet")
truncate := !c.Bool("notruncate")
JSONContainers := []jsonContainer{}
jsonOut := c.Bool("json")
if c.IsSet("quiet") && c.IsSet("format") {
return errors.Errorf("quiet and format are mutually exclusive")
}
list := func(n int, containerID, imageID, image, container string, isBuilder bool) {
if jsonOut {
JSONContainers = append(JSONContainers, jsonContainer{ID: containerID, Builder: isBuilder, ImageID: imageID, ImageName: image, ContainerName: container})
return
}
opts := containerOptions{
all: c.Bool("all"),
format: c.String("format"),
json: c.Bool("json"),
noHeading: c.Bool("noheading"),
noTruncate: c.Bool("notruncate"),
quiet: c.Bool("quiet"),
}
if n == 0 && !c.Bool("noheading") && !quiet {
if truncate {
fmt.Printf("%-12s %-8s %-12s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
} else {
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
}
}
if quiet {
fmt.Printf("%s\n", containerID)
} else {
isBuilderValue := ""
if isBuilder {
isBuilderValue = " *"
}
if truncate {
fmt.Printf("%-12.12s %-8s %-12.12s %-32s %s\n", containerID, isBuilderValue, imageID, image, container)
} else {
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", containerID, isBuilderValue, imageID, image, container)
}
var params *containerFilterParams
if c.IsSet("filter") {
params, err = parseCtrFilter(c.String("filter"))
if err != nil {
return errors.Wrapf(err, "error parsing filter")
}
}
if !opts.noHeading && !opts.quiet && opts.format == "" && !opts.json {
containerOutputHeader(!opts.noTruncate)
}
return outputContainers(store, opts, params)
}
func outputContainers(store storage.Store, opts containerOptions, params *containerFilterParams) error {
seenImages := make(map[string]string)
imageNameForID := func(id string) string {
if id == "" {
@@ -112,12 +144,36 @@ func containersCmd(c *cli.Context) error {
if err != nil {
return errors.Wrapf(err, "error reading build containers")
}
if !c.Bool("all") {
for i, builder := range builders {
var (
containerOutput []containerOutputParams
JSONContainers []jsonContainer
)
if !opts.all {
// only output containers created by buildah
for _, builder := range builders {
image := imageNameForID(builder.FromImageID)
list(i, builder.ContainerID, builder.FromImageID, image, builder.Container, true)
if !matchesCtrFilter(builder.ContainerID, builder.Container, builder.FromImageID, image, params) {
continue
}
if opts.json {
JSONContainers = append(JSONContainers, jsonContainer{ID: builder.ContainerID,
Builder: true,
ImageID: builder.FromImageID,
ImageName: image,
ContainerName: builder.Container})
continue
}
output := containerOutputParams{
ContainerID: builder.ContainerID,
Builder: " *",
ImageID: builder.FromImageID,
ImageName: image,
ContainerName: builder.Container,
}
containerOutput = append(containerOutput, output)
}
} else {
// output all containers currently in storage
builderMap := make(map[string]struct{})
for _, builder := range builders {
builderMap[builder.ContainerID] = struct{}{}
@@ -126,22 +182,136 @@ func containersCmd(c *cli.Context) error {
if err2 != nil {
return errors.Wrapf(err2, "error reading list of all containers")
}
for i, container := range containers {
for _, container := range containers {
name := ""
if len(container.Names) > 0 {
name = container.Names[0]
}
_, ours := builderMap[container.ID]
list(i, container.ID, container.ImageID, imageNameForID(container.ImageID), name, ours)
builder := ""
if ours {
builder = " *"
}
if !matchesCtrFilter(container.ID, name, container.ImageID, imageNameForID(container.ImageID), params) {
continue
}
if opts.json {
JSONContainers = append(JSONContainers, jsonContainer{ID: container.ID,
Builder: ours,
ImageID: container.ImageID,
ImageName: imageNameForID(container.ImageID),
ContainerName: name})
}
output := containerOutputParams{
ContainerID: container.ID,
Builder: builder,
ImageID: container.ImageID,
ImageName: imageNameForID(container.ImageID),
ContainerName: name,
}
containerOutput = append(containerOutput, output)
}
}
if jsonOut {
if opts.json {
data, err := json.MarshalIndent(JSONContainers, "", " ")
if err != nil {
return err
}
fmt.Printf("%s\n", data)
return nil
}
for _, ctr := range containerOutput {
if opts.quiet {
fmt.Printf("%-64s\n", ctr.ContainerID)
continue
}
if opts.format != "" {
if err := containerOutputUsingTemplate(opts.format, ctr); err != nil {
return err
}
continue
}
containerOutputUsingFormatString(!opts.noTruncate, ctr)
}
return nil
}
func containerOutputUsingTemplate(format string, params containerOutputParams) error {
tmpl, err := template.New("container").Parse(format)
if err != nil {
return errors.Wrapf(err, "Template parsing error")
}
err = tmpl.Execute(os.Stdout, params)
if err != nil {
return err
}
fmt.Println()
return nil
}
func containerOutputUsingFormatString(truncate bool, params containerOutputParams) {
if truncate {
fmt.Printf("%-12.12s %-8s %-12.12s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, params.ImageName, params.ContainerName)
} else {
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, params.ImageName, params.ContainerName)
}
}
func containerOutputHeader(truncate bool) {
if truncate {
fmt.Printf("%-12s %-8s %-12s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
} else {
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
}
}
func parseCtrFilter(filter string) (*containerFilterParams, error) {
params := new(containerFilterParams)
filters := strings.Split(filter, ",")
for _, param := range filters {
pair := strings.SplitN(param, "=", 2)
if len(pair) != 2 {
return nil, errors.Errorf("incorrect filter value %q, should be of form filter=value", param)
}
switch strings.TrimSpace(pair[0]) {
case "id":
params.id = pair[1]
case "name":
params.name = pair[1]
case "ancestor":
params.ancestor = pair[1]
default:
return nil, errors.Errorf("invalid filter %q", pair[0])
}
}
return params, nil
}
func matchesCtrName(ctrName, argName string) bool {
return strings.Contains(ctrName, argName)
}
func matchesAncestor(imgName, imgID, argName string) bool {
if matchesID(imgID, argName) {
return true
}
return matchesReference(imgName, argName)
}
func matchesCtrFilter(ctrID, ctrName, imgID, imgName string, params *containerFilterParams) bool {
if params == nil {
return true
}
if params.id != "" && !matchesID(ctrID, params.id) {
return false
}
if params.name != "" && !matchesCtrName(ctrName, params.name) {
return false
}
if params.ancestor != "" && !matchesAncestor(imgName, imgID, params.ancestor) {
return false
}
return true
}

View File

@@ -23,7 +23,7 @@ var (
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "use `username[:password]` for accessing the registry",
Usage: "use `[username[:password]]` for accessing the registry",
},
cli.StringFlag{
Name: "name",

View File

@@ -14,6 +14,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
type jsonImage struct {
@@ -51,7 +52,7 @@ var (
},
cli.StringFlag{
Name: "format",
Usage: "pretty-print images using a Go template. will override --quiet",
Usage: "pretty-print images using a Go template",
},
cli.BoolFlag{
Name: "json",
@@ -96,6 +97,10 @@ func imagesCmd(c *cli.Context) error {
return errors.Wrapf(err, "error reading images")
}
if c.IsSet("quiet") && c.IsSet("format") {
return errors.Errorf("quiet and format are mutually exclusive")
}
quiet := c.Bool("quiet")
truncate := !c.Bool("no-trunc")
digests := c.Bool("digests")
@@ -125,8 +130,6 @@ func imagesCmd(c *cli.Context) error {
if err != nil {
return errors.Wrapf(err, "error parsing filter")
}
} else {
params = nil
}
if len(images) > 0 && !c.Bool("noheading") && !quiet && !hasTemplate {
@@ -370,7 +373,9 @@ func outputUsingTemplate(format string, params imageOutputParams) error {
if err != nil {
return err
}
fmt.Println()
if terminal.IsTerminal(int(os.Stdout.Fd())) {
fmt.Println()
}
return nil
}

View File

@@ -253,7 +253,7 @@ func TestOutputImagesFormatString(t *testing.T) {
output, err := captureOutputWithError(func() error {
return outputImages(images[:1], "{{.ID}}", store, nil, "", true, true, false, false)
})
expectedOutput := fmt.Sprintf("%s", images[0].ID)
expectedOutput := images[0].ID
if err != nil {
t.Error("format string output produces error")
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/pkg/errors"
"github.com/projectatomic/buildah"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
const (
@@ -96,13 +97,19 @@ func inspectCmd(c *cli.Context) error {
}
if c.IsSet("format") {
return t.Execute(os.Stdout, buildah.GetBuildInfo(builder))
if err := t.Execute(os.Stdout, buildah.GetBuildInfo(builder)); err != nil {
return err
}
if terminal.IsTerminal(int(os.Stdout.Fd())) {
fmt.Println()
}
return nil
}
b, err := json.MarshalIndent(builder, "", " ")
if err != nil {
return errors.Wrapf(err, "error encoding build container as json")
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if terminal.IsTerminal(int(os.Stdout.Fd())) {
enc.SetEscapeHTML(false)
}
_, err = fmt.Println(string(b))
return err
return enc.Encode(builder)
}

View File

@@ -12,6 +12,7 @@ import (
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/projectatomic/buildah"
"github.com/projectatomic/buildah/util"
"github.com/urfave/cli"
)
@@ -29,7 +30,7 @@ var (
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "use `username[:password]` for accessing the registry",
Usage: "use `[username[:password]]` for accessing the registry",
},
cli.BoolFlag{
Name: "disable-compression, D",
@@ -141,7 +142,10 @@ func pushCmd(c *cli.Context) error {
err = buildah.Push(src, dest, options)
if err != nil {
return errors.Wrapf(err, "error pushing image %q to %q", src, destSpec)
return util.GetFailureCause(
err,
errors.Wrapf(err, "error pushing image %q to %q", src, destSpec),
)
}
return nil

View File

@@ -2,6 +2,7 @@ package main
import (
"fmt"
"io"
"os"
"github.com/pkg/errors"
@@ -10,19 +11,35 @@ import (
var (
rmDescription = "Removes one or more working containers, unmounting them if necessary"
rmCommand = cli.Command{
rmFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "remove all containers",
},
}
rmCommand = cli.Command{
Name: "rm",
Aliases: []string{"delete"},
Usage: "Remove one or more working containers",
Description: rmDescription,
Action: rmCmd,
ArgsUsage: "CONTAINER-NAME-OR-ID [...]",
Flags: rmFlags,
}
)
// writeError writes `lastError` into `w` if not nil and return the next error `err`
func writeError(w io.Writer, err error, lastError error) error {
if lastError != nil {
fmt.Fprintln(w, lastError)
}
return err
}
func rmCmd(c *cli.Context) error {
delContainerErrStr := "error removing container"
args := c.Args()
if len(args) == 0 {
if len(args) == 0 && !c.Bool("all") {
return errors.Errorf("container ID must be specified")
}
store, err := getStore(c)
@@ -30,28 +47,36 @@ func rmCmd(c *cli.Context) error {
return err
}
var e error
for _, name := range args {
builder, err := openBuilder(store, name)
if e == nil {
e = err
}
var lastError error
if c.Bool("all") {
builders, err := openBuilders(store)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading build container %q: %v\n", name, err)
continue
return errors.Wrapf(err, "error reading build containers")
}
id := builder.ContainerID
err = builder.Delete()
if e == nil {
e = err
for _, builder := range builders {
id := builder.ContainerID
if err = builder.Delete(); err != nil {
lastError = writeError(os.Stderr, errors.Wrapf(err, "%s %q", delContainerErrStr, builder.Container), lastError)
continue
}
fmt.Printf("%s\n", id)
}
if err != nil {
fmt.Fprintf(os.Stderr, "error removing container %q: %v\n", builder.Container, err)
continue
} else {
for _, name := range args {
builder, err := openBuilder(store, name)
if err != nil {
lastError = writeError(os.Stderr, errors.Wrapf(err, "%s %q", delContainerErrStr, name), lastError)
continue
}
id := builder.ContainerID
if err = builder.Delete(); err != nil {
lastError = writeError(os.Stderr, errors.Wrapf(err, "%s %q", delContainerErrStr, name), lastError)
continue
}
fmt.Printf("%s\n", id)
}
fmt.Printf("%s\n", id)
}
return e
return lastError
}

View File

@@ -2,6 +2,7 @@ package main
import (
"fmt"
"os"
is "github.com/containers/image/storage"
"github.com/containers/image/transports"
@@ -16,9 +17,17 @@ import (
var (
rmiDescription = "removes one or more locally stored images."
rmiFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "remove all images",
},
cli.BoolFlag{
Name: "prune, p",
Usage: "prune dangling images",
},
cli.BoolFlag{
Name: "force, f",
Usage: "force removal of the image",
Usage: "force removal of the image and any containers using the image",
},
}
rmiCommand = cli.Command{
@@ -33,11 +42,20 @@ var (
func rmiCmd(c *cli.Context) error {
force := c.Bool("force")
removeAll := c.Bool("all")
pruneDangling := c.Bool("prune")
args := c.Args()
if len(args) == 0 {
if len(args) == 0 && !removeAll && !pruneDangling {
return errors.Errorf("image name or ID must be specified")
}
if len(args) > 0 && removeAll {
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
}
if removeAll && pruneDangling {
return errors.Errorf("when using the --all switch, you may not use --prune switch")
}
if err := validateFlags(c, rmiFlags); err != nil {
return err
}
@@ -47,26 +65,62 @@ func rmiCmd(c *cli.Context) error {
return err
}
for _, id := range args {
image, err := getImage(id, store)
imagesToDelete := args[:]
var lastError error
if removeAll {
imagesToDelete, err = findAllImages(store)
if err != nil {
return errors.Wrapf(err, "could not get image %q", id)
return err
}
}
if pruneDangling {
imagesToDelete, err = findDanglingImages(store)
if err != nil {
return err
}
}
for _, id := range imagesToDelete {
image, err := getImage(id, store)
if err != nil || image == nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
if err == nil {
err = storage.ErrNotAnImage
}
lastError = errors.Wrapf(err, "could not get image %q", id)
continue
}
if image != nil {
ctrIDs, err := runningContainers(image, store)
if err != nil {
return errors.Wrapf(err, "error getting running containers for image %q", id)
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error getting running containers for image %q", id)
continue
}
if len(ctrIDs) > 0 && len(image.Names) <= 1 {
if force {
err = removeContainers(ctrIDs, store)
if err != nil {
return errors.Wrapf(err, "error removing containers %v for image %q", ctrIDs, id)
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error removing containers %v for image %q", ctrIDs, id)
continue
}
} else {
for _, ctrID := range ctrIDs {
return fmt.Errorf("Could not remove image %q (must force) - container %q is using its reference image", id, ctrID)
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(storage.ErrImageUsedByContainer, "Could not remove image %q (must force) - container %q is using its reference image", id, ctrID)
}
continue
}
}
// If the user supplied an ID, we cannot delete the image if it is referred to by multiple tags
@@ -79,7 +133,11 @@ func rmiCmd(c *cli.Context) error {
} else {
name, err2 := untagImage(id, image, store)
if err2 != nil {
return errors.Wrapf(err, "error removing tag %q from image %q", id, image.ID)
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err2, "error removing tag %q from image %q", id, image.ID)
continue
}
fmt.Printf("untagged: %s\n", name)
}
@@ -89,13 +147,17 @@ func rmiCmd(c *cli.Context) error {
}
id, err := removeImage(image, store)
if err != nil {
return errors.Wrapf(err, "error removing image %q", image.ID)
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error removing image %q", image.ID)
continue
}
fmt.Printf("%s\n", id)
}
}
return nil
return lastError
}
func getImage(id string, store storage.Store) (*storage.Image, error) {
@@ -178,7 +240,7 @@ func removeContainers(ctrIDs []string, store storage.Store) error {
func properImageRef(id string) (types.ImageReference, error) {
var err error
if ref, err := alltransports.ParseImageName(id); err == nil {
if img, err2 := ref.NewImage(nil); err2 == nil {
if img, err2 := ref.NewImageSource(nil); err2 == nil {
img.Close()
return ref, nil
}
@@ -192,7 +254,7 @@ func properImageRef(id string) (types.ImageReference, error) {
func storageImageRef(store storage.Store, id string) (types.ImageReference, error) {
var err error
if ref, err := is.Transport.ParseStoreReference(store, id); err == nil {
if img, err2 := ref.NewImage(nil); err2 == nil {
if img, err2 := ref.NewImageSource(nil); err2 == nil {
img.Close()
return ref, nil
}
@@ -211,7 +273,7 @@ func storageImageID(store storage.Store, id string) (types.ImageReference, error
imageID = img.ID
}
if ref, err := is.Transport.ParseStoreReference(store, imageID); err == nil {
if img, err2 := ref.NewImage(nil); err2 == nil {
if img, err2 := ref.NewImageSource(nil); err2 == nil {
img.Close()
return ref, nil
}
@@ -219,3 +281,35 @@ func storageImageID(store storage.Store, id string) (types.ImageReference, error
}
return nil, errors.Wrapf(err, "error parsing %q as a storage image reference: %v", id)
}
// Returns a list of all existing images
func findAllImages(store storage.Store) ([]string, error) {
imagesToDelete := []string{}
images, err := store.Images()
if err != nil {
return nil, errors.Wrapf(err, "error reading images")
}
for _, image := range images {
imagesToDelete = append(imagesToDelete, image.ID)
}
return imagesToDelete, nil
}
// Returns a list of all dangling images
func findDanglingImages(store storage.Store) ([]string, error) {
imagesToDelete := []string{}
images, err := store.Images()
if err != nil {
return nil, errors.Wrapf(err, "error reading images")
}
for _, image := range images {
if len(image.Names) == 0 {
imagesToDelete = append(imagesToDelete, image.ID)
}
}
return imagesToDelete, nil
}

View File

@@ -17,7 +17,7 @@ var (
runFlags = []cli.Flag{
cli.StringFlag{
Name: "hostname",
Usage: "Set the hostname inside of the container",
Usage: "set the hostname inside of the container",
},
cli.StringFlag{
Name: "runtime",
@@ -28,6 +28,10 @@ var (
Name: "runtime-flag",
Usage: "add global flags for the container runtime",
},
cli.StringSliceFlag{
Name: "security-opt",
Usage: "security Options (default [])",
},
cli.BoolFlag{
Name: "tty",
Usage: "allocate a pseudo-TTY in the container",
@@ -73,10 +77,15 @@ func runCmd(c *cli.Context) error {
return errors.Wrapf(err, "error reading build container %q", name)
}
runtimeFlags := []string{}
for _, arg := range c.StringSlice("runtime-flag") {
runtimeFlags = append(runtimeFlags, "--"+arg)
}
options := buildah.RunOptions{
Hostname: c.String("hostname"),
Runtime: c.String("runtime"),
Args: c.StringSlice("runtime-flag"),
Args: runtimeFlags,
}
if c.IsSet("tty") {

View File

@@ -2,7 +2,6 @@ package buildah
import (
"encoding/json"
"fmt"
"path/filepath"
"runtime"
"strings"
@@ -139,23 +138,30 @@ func makeDockerV2S1Image(manifest docker.V2S1Manifest) (docker.V2Image, error) {
}
// Build a filesystem history.
history := []docker.V2S2History{}
lastID := ""
for i := range manifest.History {
h := docker.V2S2History{
Created: time.Now().UTC(),
Author: "",
CreatedBy: "",
Comment: "",
EmptyLayer: false,
}
// Decode the compatibility field.
dcompat := docker.V1Compatibility{}
if err2 := json.Unmarshal([]byte(manifest.History[i].V1Compatibility), &dcompat); err2 == nil {
h.Created = dcompat.Created.UTC()
h.Author = dcompat.Author
h.Comment = dcompat.Comment
if len(dcompat.ContainerConfig.Cmd) > 0 {
h.CreatedBy = fmt.Sprintf("%v", dcompat.ContainerConfig.Cmd)
}
h.EmptyLayer = dcompat.ThrowAway
if err = json.Unmarshal([]byte(manifest.History[i].V1Compatibility), &dcompat); err != nil {
return docker.V2Image{}, errors.Errorf("error parsing image compatibility data (%q) from history", manifest.History[i].V1Compatibility)
}
// Skip this history item if it shares the ID of the last one
// that we saw, since the image library will do the same.
if i > 0 && dcompat.ID == lastID {
continue
}
lastID = dcompat.ID
// Construct a new history item using the recovered information.
createdBy := ""
if len(dcompat.ContainerConfig.Cmd) > 0 {
createdBy = strings.Join(dcompat.ContainerConfig.Cmd, " ")
}
h := docker.V2S2History{
Created: dcompat.Created.UTC(),
Author: dcompat.Author,
CreatedBy: createdBy,
Comment: dcompat.Comment,
EmptyLayer: dcompat.ThrowAway,
}
// Prepend this layer to the list, because a v2s1 format manifest's list is in reverse order
// compared to v2s2, which lists earlier layers before later ones.

View File

@@ -210,6 +210,10 @@ return 1
_buildah_rmi() {
local boolean_options="
--all
-a
--force
-f
--help
-h
"
@@ -226,6 +230,8 @@ return 1
_buildah_rm() {
local boolean_options="
--all
-a
--help
-h
"
@@ -296,6 +302,7 @@ return 1
"
local options_with_args="
--authfile
--cert-dir
--creds
--signature-policy
@@ -346,15 +353,19 @@ return 1
local options_with_args="
--authfile
--signature-policy
--build-arg
--cert-dir
--creds
-f
--file
--format
--label
--runtime
--runtime-flag
--tag
--security-opt
--signature-policy
-t
--file
-f
--build-arg
--format
--tag
"
local all_options="$options_with_args $boolean_options"
@@ -390,6 +401,7 @@ return 1
--hostname
--runtime
--runtime-flag
--security-opt
--volume
-v
"
@@ -554,6 +566,9 @@ return 1
"
local options_with_args="
--filter
-f
--format
"
local all_options="$options_with_args $boolean_options"

View File

@@ -25,7 +25,7 @@
%global shortcommit %(c=%{commit}; echo ${c:0:7})
Name: buildah
Version: 0.10
Version: 0.11
Release: 1.git%{shortcommit}%{?dist}
Summary: A command line tool used to creating OCI Images
License: ASL 2.0
@@ -89,6 +89,37 @@ make DESTDIR=%{buildroot} PREFIX=%{_prefix} install install.completions
%{_datadir}/bash-completion/completions/*
%changelog
* Mon Feb 12 2018 Dan Walsh <dwalsh@redhat.com> 0.12-1
- Added handing for simpler error message for Unknown Dockerfile instructions.
- Change default certs directory to /etc/containers/certs.dir
- Vendor in latest containers/image
- Vendor in latest containers/storage
- build-using-dockerfile: set the 'author' field for MAINTAINER
- Return exit code 1 when buildah-rmi fails
- Trim the image reference to just its name before calling getImageName
- Touch up rmi -f usage statement
- Add --format and --filter to buildah containers
- Add --prune,-p option to rmi command
- Add authfile param to commit
- Fix --runtime-flag for buildah run and bud
- format should override quiet for images
- Allow all auth params to work with bud
- Do not overwrite directory permissions on --chown
- Unescape HTML characters output into the terminal
- Fix: setting the container name to the image
- Prompt for un/pwd if not supplied with --creds
- Make bud be really quiet
- Return a better error message when failed to resolve an image
- Update auth tests and fix bud man page
* Tue Jan 16 2018 Dan Walsh <dwalsh@redhat.com> 0.11-1
- Add --all to remove containers
- Add --all functionality to rmi
- Show ctrid when doing rm -all
- Ignore sequential duplicate layers when reading v2s1
- Lots of minor bug fixes
- Vendor in latest containers/image and containers/storage
* Sat Dec 23 2017 Dan Walsh <dwalsh@redhat.com> 0.10-1
- Display Config and Manifest as strings
- Bump containers/image

View File

@@ -13,10 +13,9 @@ build context directory. The build context directory can be specified as the
to a temporary location.
## OPTIONS
**--authfile** *path*
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `kpod login`.
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--build-arg** *arg=value*
@@ -26,6 +25,17 @@ instructions read from the Dockerfiles in the same way that environment
variables are, but which will not be added to environment variable list in the
resulting image's configuration.
**--cert-dir** *path*
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry.
Default certificates directory is _/etc/containers/certs.d_.
**--creds** *creds*
The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
**-f, --file** *Dockerfile*
Specifies a Dockerfile which contains instructions for building the image,
@@ -53,7 +63,7 @@ Defaults to *true*.
Pull the image even if a version of the image is already present.
**--quiet**
**-q, --quiet**
Suppress output messages which indicate which instruction is being processed,
and of progress when pulling images from a registry, and when writing the
@@ -66,7 +76,11 @@ commands specified by the **RUN** instruction.
**--runtime-flag** *flag*
Adds global flags for the container rutime.
Adds global flags for the container rutime. To list the supported flags, please
consult manpages of your selected container runtime (`runc` is the default
runtime, the manpage to consult is `runc(8)`).
Note: Do not pass the leading `--` to the flag. To pass the runc flag `--log-format json`
to buildah bud, the option given would be `--runtime-flag log-format=json`.
**--signature-policy** *signaturepolicy*
@@ -97,5 +111,11 @@ buildah bud --tls-verify=true -t imageName -f Dockerfile.simple
buildah bud --tls-verify=false -t imageName .
buildah bud --runtime-flag log-format=json .
buildah bud --runtime-flag debug .
buildah bud --authfile /tmp/auths/myauths.json --cert-dir ~/auth --tls-verify=true --creds=username:password -t imageName -f Dockerfile.simple
## SEE ALSO
buildah(1), kpod-login(1), docker-login(1)
buildah(1), podman-login(1), docker-login(1)

View File

@@ -13,13 +13,21 @@ specified, an ID is assigned, but no name is assigned to the image.
## OPTIONS
**--authfile** *path*
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--cert-dir** *path*
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry.
Default certificates directory is _/etc/containers/certs.d_.
**--creds** *creds*
The username[:password] to use to authenticate with the registry if required.
The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
**--disable-compression, -D**
@@ -71,5 +79,8 @@ This example commits the container to the image on the local registry while turn
This example commits the container to the image on the local registry using credentials and certificates for authentication.
`buildah commit --cert-dir ~/auth --tls-verify=true --creds=username:password containerID docker://localhost:5000/imageId`
This example commits the container to the image on the local registry using credentials from the /tmp/auths/myauths.json file and certificates for authentication.
`buildah commit --authfile /tmp/auths/myauths.json --cert-dir ~/auth --tls-verify=true --creds=username:password containerID docker://localhost:5000/imageId`
## SEE ALSO
buildah(1)

View File

@@ -15,7 +15,34 @@ IDs, and the names and IDs of the images from which they were initialized.
**--all, -a**
List information about all containers, including those which were not created
by and are not being used by Buildah.
by and are not being used by Buildah. Containers created by Buildah are
denoted with an '*' in the 'BUILDER' column.
**--filter, -f**
Filter output based on conditions provided.
Valid filters are listed below:
| **Filter** | **Description** |
| --------------- | ------------------------------------------------------------------- |
| id | [ID] Container's ID |
| name | [Name] Container's name |
| ancestor | [ImageName] Image or descendant used to create container |
**--format**
Pretty-print containers using a Go template.
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | -----------------------------------------|
| .ContainerID | Container ID |
| .Builder | Whether container was created by buildah |
| .ImageID | Image ID |
| .ImageName | Image name |
| .ContainerName | Container name |
**--json**
@@ -36,12 +63,55 @@ Displays only the container IDs.
## EXAMPLE
buildah containers
```
CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME
29bdb522fc62 * 3fd9065eaf02 docker.io/library/alpine:latest alpine-working-container
c6b04237ac8e * f9b6f7f7b9d3 docker.io/library/busybox:latest busybox-working-container
```
buildah containers --quiet
```
29bdb522fc62d43fca0c1a0f11cfc6dfcfed169cf6cf25f928ebca1a612ff5b0
c6b04237ac8e9d435ec9cf0e7eda91e302f2db9ef908418522c2d666352281eb
```
buildah containers -q --noheading --notruncate
```
29bdb522fc62d43fca0c1a0f11cfc6dfcfed169cf6cf25f928ebca1a612ff5b0
c6b04237ac8e9d435ec9cf0e7eda91e302f2db9ef908418522c2d666352281eb
```
buildah containers --json
```
[
{
"id": "29bdb522fc62d43fca0c1a0f11cfc6dfcfed169cf6cf25f928ebca1a612ff5b0",
"builder": true,
"imageid": "3fd9065eaf02feaf94d68376da52541925650b81698c53c6824d92ff63f98353",
"imagename": "docker.io/library/alpine:latest",
"containername": "alpine-working-container"
},
{
"id": "c6b04237ac8e9d435ec9cf0e7eda91e302f2db9ef908418522c2d666352281eb",
"builder": true,
"imageid": "f9b6f7f7b9d34113f66e16a9da3e921a580937aec98da344b852ca540aaa2242",
"imagename": "docker.io/library/busybox:latest",
"containername": "busybox-working-container"
}
]
```
buildah containers --format "{{.ContainerID}} {{.ContainerName}}"
```
3fbeaa87e583ee7a3e6787b2d3af961ef21946a0c01a08938e4f52d53cce4c04 myalpine-working-container
fbfd3505376ee639c3ed50f9d32b78445cd59198a1dfcacf2e7958cda2516d5c ubuntu-working-container
```
buildah containers --filter ancestor=ubuntu
```
CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME
fbfd3505376e * 0ff04b2e7b63 docker.io/library/ubuntu:latest ubuntu-working-container
```
## SEE ALSO
buildah(1)

View File

@@ -17,7 +17,7 @@ Multiple transports are supported:
An existing local directory _path_ retrieving the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
**docker://**_docker-reference_ (Default)
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(kpod login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
**docker-archive:**_path_
An image is retrieved as a `docker load` formatted file.
@@ -38,16 +38,19 @@ The container ID of the container that was created. On error, -1 is returned an
**--authfile** *path*
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `kpod login`.
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--cert-dir** *path*
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry.
Default certificates directory is _/etc/containers/certs.d_.
**--creds** *creds*
The username[:password] to use to authenticate with the registry if required.
The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
**--name** *name*
@@ -94,4 +97,4 @@ buildah from myregistry/myrepository/imagename:imagetag --creds=myusername:mypas
buildah from myregistry/myrepository/imagename:imagetag --authfile=/tmp/auths/myauths.json
## SEE ALSO
buildah(1), kpod-login(1), docker-login(1)
buildah(1), podman-login(1), docker-login(1)

View File

@@ -22,7 +22,7 @@ keywords are 'dangling', 'label', 'before' and 'since'.
**--format="TEMPLATE"**
Pretty-print images using a Go template. Will override --quiet
Pretty-print images using a Go template.
**--json**

View File

@@ -24,7 +24,7 @@ Image stored in local container/storage
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
**docker://**_docker-reference_
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(kpod login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
**docker-archive:**_path_[**:**_docker-reference_]
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
@@ -42,16 +42,19 @@ Image stored in local container/storage
**--authfile** *path*
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `kpod login`.
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--cert-dir** *path*
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry
Use certificates at *path* (*.crt, *.cert, *.key) to connect to the registry.
Default certificates directory is _/etc/containers/certs.d_.
**--creds** *creds*
The username[:password] to use to authenticate with the registry if required.
The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
**--disable-compression, -D**
@@ -104,4 +107,4 @@ This example extracts the imageID image and puts it into the registry on the loc
`# buildah push --cert-dir ~/auth --tls-verify=true --creds=username:password imageID docker://localhost:5000/my-imageID`
## SEE ALSO
buildah(1), kpod-login(1), docker-login(1)
buildah(1), podman-login(1), docker-login(1)

View File

@@ -9,11 +9,19 @@ buildah rm - Removes one or more working containers.
## DESCRIPTION
Removes one or more working containers, unmounting them if necessary.
## OPTIONS
**--all, -a**
All Buildah containers will be removed. Buildah containers are denoted with an '*' in the 'BUILDER' column listed by the command 'buildah containers'.
## EXAMPLE
buildah rm containerID
buildah rm containerID1 containerID2 containerID3
buildah rm --all
## SEE ALSO
buildah(1)

View File

@@ -9,16 +9,35 @@ buildah rmi - Removes one or more images.
## DESCRIPTION
Removes one or more locally stored images.
## LIMITATIONS
If the image was pushed to a directory path using the 'dir:' transport
the rmi command can not remove the image. Instead standard file system
commands should be used.
## OPTIONS
**--all, -a**
All local images will be removed from the system that do not have containers using the image as a reference image.
**--prune, -p**
All local images will be removed from the system that do not have a tag and do not have a child image pointing to them.
**--force, -f**
Executing this command will stop all containers that are using the image and remove them from the system
This option will cause Buildah to remove all containers that are using the image before removing the image from the system.
## EXAMPLE
buildah rmi imageID
buildah rmi --all
buildah rmi --all --force
buildah rmi --prune
buildah rmi --force imageID
buildah rmi imageID1 imageID2 imageID3

View File

@@ -14,7 +14,6 @@ the *buildah config* command. If you execute *buildah run* and expect an
interactive shell, you need to specify the --tty flag.
## OPTIONS
**--hostname**
Set the hostname inside of the running container.
@@ -26,7 +25,9 @@ The *path* to an alternate OCI-compatible runtime.
Adds global flags for the container runtime. To list the supported flags, please
consult manpages of your selected container runtime (`runc` is the default
runtime, the manpage to consult is `runc(8)`)
runtime, the manpage to consult is `runc(8)`).
Note: Do not pass the leading `--` to the flag. To pass the runc flag `--log-format json`
to buildah run, the option given would be `--runtime-flag log-format=json`.
**--tty**
@@ -49,7 +50,9 @@ buildah run containerID -- ps -auxw
buildah run containerID --hostname myhost -- ps -auxw
buildah run containerID --runtime-flag --no-new-keyring -- ps -auxw
buildah run --runtime-flag log-format=json containerID /bin/bash
buildah run --runtime-flag debug containerID /bin/bash
buildah run --tty containerID /bin/bash

View File

@@ -16,6 +16,8 @@ The Buildah package provides a command line tool which can be used to:
* Use the updated contents of a container's root filesystem as a filesystem layer to create a new image.
* Delete a working container or an image.
This tool needs to be run as the root user.
## OPTIONS
**--debug**

View File

@@ -73,6 +73,26 @@ func (i *containerImageRef) NewImage(sc *types.SystemContext) (types.ImageCloser
return image.FromSource(sc, src)
}
func expectedOCIDiffIDs(image v1.Image) int {
expected := 0
for _, history := range image.History {
if !history.EmptyLayer {
expected = expected + 1
}
}
return expected
}
func expectedDockerDiffIDs(image docker.V2Image) int {
expected := 0
for _, history := range image.History {
if !history.EmptyLayer {
expected = expected + 1
}
}
return expected
}
func (i *containerImageRef) NewImageSource(sc *types.SystemContext) (src types.ImageSource, err error) {
// Decide which type of manifest and configuration output we're going to provide.
manifestType := i.preferredManifestType
@@ -207,6 +227,10 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext) (src types.I
// Until the image specs define a media type for bzip2-compressed layers, even if we know
// how to decompress them, we can't try to compress layers with bzip2.
return nil, errors.New("media type for bzip2-compressed layers is not defined")
case archive.Xz:
// Until the image specs define a media type for xz-compressed layers, even if we know
// how to decompress them, we can't try to compress layers with xz.
return nil, errors.New("media type for xz-compressed layers is not defined")
default:
logrus.Debugf("compressing layer %q with unknown compressor(?)", layerID)
}
@@ -290,6 +314,17 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext) (src types.I
}
dimage.History = append(dimage.History, dnews)
// Sanity check that we didn't just create a mismatch between non-empty layers in the
// history and the number of diffIDs.
expectedDiffIDs := expectedOCIDiffIDs(oimage)
if len(oimage.RootFS.DiffIDs) != expectedDiffIDs {
return nil, errors.Errorf("internal error: history lists %d non-empty layers, but we have %d layers on disk", expectedDiffIDs, len(oimage.RootFS.DiffIDs))
}
expectedDiffIDs = expectedDockerDiffIDs(dimage)
if len(dimage.RootFS.DiffIDs) != expectedDiffIDs {
return nil, errors.Errorf("internal error: history lists %d non-empty layers, but we have %d layers on disk", expectedDiffIDs, len(dimage.RootFS.DiffIDs))
}
// Encode the image configuration blob.
oconfig, err := json.Marshal(&oimage)
if err != nil {

View File

@@ -95,8 +95,6 @@ type BuildOptions struct {
// specified, indicating that the shared, system-wide default policy
// should be used.
SignaturePolicyPath string
// SkipTLSVerify denotes whether TLS verification should not be used.
SkipTLSVerify bool
// ReportWriter is an io.Writer which will be used to report the
// progress of the (possible) pulling of the source image and the
// writing of the new image.
@@ -105,7 +103,8 @@ type BuildOptions struct {
// configuration data.
// Accepted values are OCIv1ImageFormat and Dockerv2ImageFormat.
OutputFormat string
AuthFilePath string
// SystemContext holds parameters used for authentication.
SystemContext *types.SystemContext
}
// Executor is a buildah-based implementation of the imagebuilder.Executor
@@ -139,18 +138,6 @@ type Executor struct {
reportWriter io.Writer
}
func makeSystemContext(signaturePolicyPath, authFilePath string, skipTLSVerify bool) *types.SystemContext {
sc := &types.SystemContext{}
if signaturePolicyPath != "" {
sc.SignaturePolicyPath = signaturePolicyPath
}
if authFilePath != "" {
sc.AuthFilePath = authFilePath
}
sc.DockerInsecureSkipTLSVerify = skipTLSVerify
return sc
}
// Preserve informs the executor that from this point on, it needs to ensure
// that only COPY and ADD instructions can modify the contents of this
// directory or anything below it.
@@ -380,6 +367,7 @@ func (b *Executor) Run(run imagebuilder.Run, config docker.Config) error {
Entrypoint: config.Entrypoint,
Cmd: config.Cmd,
NetworkDisabled: config.NetworkDisabled,
Quiet: b.quiet,
}
args := run.Args
@@ -401,12 +389,23 @@ func (b *Executor) Run(run imagebuilder.Run, config docker.Config) error {
// UnrecognizedInstruction is called when we encounter an instruction that the
// imagebuilder parser didn't understand.
func (b *Executor) UnrecognizedInstruction(step *imagebuilder.Step) error {
if !b.ignoreUnrecognizedInstructions {
logrus.Debugf("+(UNIMPLEMENTED?) %#v", step)
err_str := fmt.Sprintf("Build error: Unknown instruction: %q ", step.Command)
err := fmt.Sprintf(err_str+"%#v", step)
if b.ignoreUnrecognizedInstructions {
logrus.Debugf(err)
return nil
}
logrus.Errorf("+(UNIMPLEMENTED?) %#v", step)
return errors.Errorf("Unrecognized instruction: %#v", step)
switch logrus.GetLevel() {
case logrus.ErrorLevel:
logrus.Errorf(err_str)
case logrus.DebugLevel:
logrus.Debugf(err)
default:
logrus.Errorf("+(UNHANDLED LOGLEVEL) %#v", step)
}
return errors.Errorf(err)
}
// NewExecutor creates a new instance of the imagebuilder.Executor interface.
@@ -427,7 +426,7 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {
outputFormat: options.OutputFormat,
additionalTags: options.AdditionalTags,
signaturePolicyPath: options.SignaturePolicyPath,
systemContext: makeSystemContext(options.SignaturePolicyPath, options.AuthFilePath, options.SkipTLSVerify),
systemContext: options.SystemContext,
volumeCache: make(map[string]string),
volumeCacheInfo: make(map[string]os.FileInfo),
log: options.Log,
@@ -475,6 +474,7 @@ func (b *Executor) Prepare(ib *imagebuilder.Builder, node *parser.Node, from str
Transport: b.transport,
SignaturePolicyPath: b.signaturePolicyPath,
ReportWriter: b.reportWriter,
SystemContext: b.systemContext,
}
builder, err := buildah.NewBuilder(b.store, builderOptions)
if err != nil {
@@ -578,6 +578,8 @@ func (b *Executor) Commit(ib *imagebuilder.Builder) (err error) {
if err2 == nil {
imageRef = imageRef2
err = nil
} else {
err = err2
}
}
} else {
@@ -586,6 +588,9 @@ func (b *Executor) Commit(ib *imagebuilder.Builder) (err error) {
if err != nil {
return errors.Wrapf(err, "error parsing reference for image to be written")
}
if ib.Author != "" {
b.builder.SetMaintainer(ib.Author)
}
config := ib.Config()
b.builder.SetHostname(config.Hostname)
b.builder.SetDomainname(config.Domainname)

View File

@@ -1,6 +1,28 @@
# Installation Instructions
Prior to installing Buildah, install the following packages on your linux distro:
## System Requirements
### Kernel Version Requirements
To run Buildah on Red Hat Enterprise Linux or CentOS, version 7.4 or higher is required.
On other Linux distributions Buildah requires a kernel version of 4.0 or
higher in order to support the OverlayFS filesystem. The kernel version can be checked
with the 'uname -a' command.
### runc Requirement
Buildah uses `runc` to run commands when `buildah run` is used, or when `buildah build-using-dockerfile`
encounters a `RUN` instruction, so you'll also need to build and install a compatible version of
[runc](https://github.com/opencontainers/runc) for Buildah to call for those cases. If Buildah is installed
via a package manager such as yum, dnf or apt-get, runc will be installed as part of that process.
## Package Installation
Buildah is available on several software repositories and can be installed via a package manager such
as yum, dnf or apt-get on a number of Linux distributions.
## Installation from GitHub
Prior to installing Buildah, install the following packages on your Linux distro:
* make
* golang (Requires version 1.8.1 or higher.)
* bats
@@ -16,7 +38,7 @@ Prior to installing Buildah, install the following packages on your linux distro
* runc (Requires version 1.0 RC4 or higher.)
* skopeo-containers
## Fedora
### Fedora
In Fedora, you can use this command:
@@ -52,7 +74,7 @@ Then to install Buildah on Fedora follow the steps in this example:
buildah --help
```
## RHEL, CentOS
### RHEL, CentOS
In RHEL and CentOS 7, ensure that you are subscribed to `rhel-7-server-rpms`,
`rhel-7-server-extras-rpms`, and `rhel-7-server-optional-rpms`, then
@@ -78,9 +100,9 @@ run this command:
The build steps for Buildah on RHEL or CentOS are the same as Fedora, above.
## Ubuntu
### Ubuntu
In Ubuntu zesty and xenial, you can use this command:
In Ubuntu zesty and xenial, you can use these commands:
```
apt-get -y install software-properties-common
@@ -104,7 +126,7 @@ Then to install Buildah on Ubuntu follow the steps in this example:
buildah --help
```
## Debian
### Debian
To install the required dependencies, you can use those commands, tested under Debian GNU/Linux amd64 9.3 (stretch):

31
new.go
View File

@@ -77,6 +77,24 @@ func pullAndFindImage(store storage.Store, imageName string, options BuilderOpti
return img, ref, nil
}
func getImageName(name string, img *storage.Image) string {
imageName := name
if len(img.Names) > 0 {
imageName = img.Names[0]
// When the image used by the container is a tagged image
// the container name might be set to the original image instead of
// the image given in the "form" command line.
// This loop is supposed to fix this.
for _, n := range img.Names {
if strings.Contains(n, name) {
imageName = n
break
}
}
}
return imageName
}
func imageNamePrefix(imageName string) string {
prefix := imageName
s := strings.Split(imageName, "/")
@@ -144,6 +162,7 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
pulledImg, pulledReference, err2 := pullAndFindImage(store, image, options, systemContext)
if err2 != nil {
logrus.Debugf("error pulling and reading image %q: %v", image, err2)
err = err2
continue
}
ref = pulledReference
@@ -155,11 +174,13 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
if err2 != nil {
if options.Transport == "" {
logrus.Debugf("error parsing image name %q: %v", image, err2)
err = err2
continue
}
srcRef2, err3 := alltransports.ParseImageName(options.Transport + image)
if err3 != nil {
logrus.Debugf("error parsing image name %q: %v", image, err2)
err = err3
continue
}
srcRef = srcRef2
@@ -186,6 +207,7 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
pulledImg, pulledReference, err2 := pullAndFindImage(store, image, options, systemContext)
if err2 != nil {
logrus.Debugf("error pulling and reading image %q: %v", image, err2)
err = err2
continue
}
ref = pulledReference
@@ -195,14 +217,15 @@ func newBuilder(store storage.Store, options BuilderOptions) (*Builder, error) {
}
if options.FromImage != "" && (ref == nil || img == nil) {
return nil, errors.Wrapf(storage.ErrImageUnknown, "no such image %q", options.FromImage)
// If options.FromImage is set but we ended up
// with nil in ref or in img then there was an error that
// we should return.
return nil, util.GetFailureCause(err, errors.Wrapf(storage.ErrImageUnknown, "no such image %q", options.FromImage))
}
image := options.FromImage
imageID := ""
if img != nil {
if len(img.Names) > 0 {
image = img.Names[0]
}
image = getImageName(imageNamePrefix(image), img)
imageID = img.ID
}
if manifest, config, err = imageManifestAndConfig(ref, systemContext); err != nil {

28
new_test.go Normal file
View File

@@ -0,0 +1,28 @@
package buildah
import (
"testing"
"github.com/containers/storage"
)
func TestGetImageName(t *testing.T) {
tt := []struct {
caseName string
name string
names []string
expected string
}{
{"tagged image", "busybox1", []string{"docker.io/library/busybox:latest", "docker.io/library/busybox1:latest"}, "docker.io/library/busybox1:latest"},
{"image name not in the resolved image names", "image1", []string{"docker.io/library/busybox:latest", "docker.io/library/busybox1:latest"}, "docker.io/library/busybox:latest"},
{"resolved image with empty name list", "image1", []string{}, "image1"},
}
for _, tc := range tt {
img := &storage.Image{Names: tc.names}
res := getImageName(tc.name, img)
if res != tc.expected {
t.Errorf("test case '%s' failed: expected %#v but got %#v", tc.caseName, tc.expected, res)
}
}
}

14
run.go
View File

@@ -65,6 +65,8 @@ type RunOptions struct {
// decision can be overridden by specifying either WithTerminal or
// WithoutTerminal.
Terminal int
// Quiet tells the run to turn off output to stdout.
Quiet bool
}
func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts []specs.Mount, bindFiles, volumes []string) error {
@@ -113,6 +115,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts
secretMounts, err := secretMounts(file, b.MountLabel, cdir)
if err != nil {
logrus.Warn("error mounting secrets, skipping...")
continue
}
for _, mount := range secretMounts {
if haveMount(mount.Destination) {
@@ -139,7 +142,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts
return errors.Wrapf(err, "error relabeling directory %q for volume %q in container %q", volumePath, volume, b.ContainerID)
}
srcPath := filepath.Join(mountPoint, volume)
if err = copyFileWithTar(srcPath, volumePath); err != nil && !os.IsNotExist(err) {
if err = copyWithTar(srcPath, volumePath); err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "error populating directory %q for volume %q in container %q using contents of %q", volumePath, volume, b.ContainerID, srcPath)
}
@@ -248,11 +251,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
return errors.Wrapf(err, "error removing network namespace for run")
}
}
if options.User != "" {
user, err = getUser(mountPoint, options.User)
} else {
user, err = getUser(mountPoint, b.User())
}
user, err = b.user(mountPoint, options.User)
if err != nil {
return err
}
@@ -289,6 +288,9 @@ func (b *Builder) Run(command []string, options RunOptions) error {
cmd.Dir = mountPoint
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
if options.Quiet {
cmd.Stdout = nil
}
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {

View File

@@ -21,8 +21,28 @@ load helpers
run buildah from localhost:5000/my-alpine --name "my-alpine" --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword
[ "$status" -eq 0 ]
# Create Dockerfile for bud tests
FILE=./Dockerfile
/bin/cat <<EOM >$FILE
FROM localhost:5000/my-alpine
EOM
chmod +x $FILE
# Remove containers and images before bud tests
buildah rm --all
buildah rmi -f --all
# bud test bad password should fail
run buildah bud -f ./Dockerfile --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds=testuser:badpassword
[ "$status" -ne 0 ]
# bud test this should work
run buildah bud -f ./Dockerfile --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds=testuser:testpassword
echo $status
[ "$status" -eq 0 ]
# Clean up
buildah rm my-alpine
buildah rm alpine
buildah rmi -f $(buildah --debug=false images -q)
rm -f ./Dockerfile
buildah rm -a
buildah rmi -f --all
}

View File

@@ -236,3 +236,29 @@ load helpers
[ "$status" -eq 0 ]
[ "$output" = "" ]
}
@test "bud-maintainer" {
target=alpine-image
buildah bud --signature-policy ${TESTSDIR}/policy.json -t ${target} ${TESTSDIR}/bud/maintainer
run buildah --debug=false inspect --type=image --format '{{.Docker.Author}}' ${target}
[ "$status" -eq 0 ]
[ "$output" = kilroy ]
run buildah --debug=false inspect --type=image --format '{{.OCIv1.Author}}' ${target}
[ "$status" -eq 0 ]
[ "$output" = kilroy ]
buildah rmi $(buildah --debug=false images -q)
run buildah --debug=false images -q
[ "$status" -eq 0 ]
[ "$output" = "" ]
}
@test "bud-unrecognized-instruction" {
target=alpine-image
run buildah bud --signature-policy ${TESTSDIR}/policy.json -t ${target} ${TESTSDIR}/bud/unrecognized
[ "$status" -ne 0 ]
[[ "$output" =~ BOGUS ]]
buildah rmi $(buildah --debug=false images -q)
run buildah --debug=false images -q
[ "$status" -eq 0 ]
[ "$output" = "" ]
}

View File

@@ -0,0 +1,2 @@
FROM alpine
MAINTAINER kilroy

View File

@@ -0,0 +1,2 @@
FROM alpine
BOGUS nope-nope-nope

View File

@@ -114,9 +114,12 @@ load helpers
@test "copy --chown" {
mkdir -p ${TESTDIR}/subdir
createrandom ${TESTDIR}/randomfile
mkdir -p ${TESTDIR}/other-subdir
createrandom ${TESTDIR}/subdir/randomfile
createrandom ${TESTDIR}/subdir/other-randomfile
createrandom ${TESTDIR}/randomfile
createrandom ${TESTDIR}/other-subdir/randomfile
createrandom ${TESTDIR}/other-subdir/other-randomfile
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json alpine)
root=$(buildah mount $cid)
@@ -129,5 +132,8 @@ load helpers
test $(stat -c "%U:%g" $root/randomfile2) = "root:1"
test $(stat -c "%U" $root/randomfile3) = "nobody"
(cd $root/subdir/; for i in *; do test $(stat -c "%U:%G" $i) = "nobody:root"; done)
buildah copy --chown root:root $cid ${TESTDIR}/other-subdir /subdir
(cd $root/subdir/; for i in *randomfile; do test $(stat -c "%U:%G" $i) = "root:root"; done)
test $(stat -c "%U:%G" $root/subdir) = "nobody:root"
buildah rm $cid
}

View File

@@ -19,6 +19,10 @@ fromreftest() {
fromreftest kubernetes/pause@sha256:f8cd50c5a287dd8c5f226cf69c60c737d34ed43726c14b8a746d9de2d23eda2b
}
@test "from-by-digest-s1-a-discarded-layer" {
fromreftest docker/whalesay@sha256:178598e51a26abbc958b8a2e48825c90bc22e641de3d31e18aaf55f3258ba93b
}
@test "from-by-tag-s1" {
fromreftest kubernetes/pause:go
}

View File

@@ -12,12 +12,10 @@ load helpers
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json dir:${elsewhere})
buildah rm $cid
buildah rmi ${elsewhere}
[ "$cid" = elsewhere-img-working-container ]
cid=$(buildah from --pull-always --signature-policy ${TESTSDIR}/policy.json dir:${elsewhere})
buildah rm $cid
buildah rmi ${elsewhere}
[ "$cid" = `basename ${elsewhere}`-working-container ]
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json scratch)
@@ -26,12 +24,10 @@ load helpers
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json dir:${elsewhere})
buildah rm $cid
buildah rmi ${elsewhere}
[ "$cid" = elsewhere-img-working-container ]
cid=$(buildah from --pull-always --signature-policy ${TESTSDIR}/policy.json dir:${elsewhere})
buildah rm $cid
buildah rmi ${elsewhere}
[ "$cid" = `basename ${elsewhere}`-working-container ]
}
@@ -73,7 +69,6 @@ load helpers
}
@test "from-authenticate-cert-and-creds" {
mkdir -p ${TESTDIR}/auth
# Create creds and store in ${TESTDIR}/auth/htpasswd
# docker run --entrypoint htpasswd registry:2 -Bbn testuser testpassword > ${TESTDIR}/auth/htpasswd
@@ -112,3 +107,24 @@ load helpers
# buildah rm $ctrid
# buildah rmi -f $(buildah --debug=false images -q)
}
@test "from-tagged-image" {
# Github #396: Make sure the container name starts with the correct image even when it's tagged.
cid=$(buildah from --pull=false --signature-policy ${TESTSDIR}/policy.json scratch)
buildah commit --signature-policy ${TESTSDIR}/policy.json "$cid" scratch2
buildah rm $cid
buildah tag scratch2 scratch3
cid=$(buildah from --signature-policy ${TESTSDIR}/policy.json scratch3)
[ "$cid" == scratch3-working-container ]
buildah rm ${cid}
buildah rmi scratch2 scratch3
# Github https://github.com/projectatomic/buildah/issues/396#issuecomment-360949396
cid=$(buildah from --pull=true --signature-policy ${TESTSDIR}/policy.json alpine)
buildah rm $cid
buildah tag alpine alpine2
cid=$(buildah from --signature-policy ${TESTSDIR}/policy.json docker.io/alpine2)
[ "$cid" == alpine2-working-container ]
buildah rm ${cid}
buildah rmi alpine alpine2
}

View File

@@ -26,3 +26,15 @@ load helpers
[ "$status" -eq 0 ]
[ "$output" != "" ]
}
@test "HTML escaped" {
cid=$(buildah from --pull=false --signature-policy ${TESTSDIR}/policy.json scratch)
buildah config --label maintainer="Darth Vader <dvader@darkside.io>" ${cid}
buildah commit --signature-policy ${TESTSDIR}/policy.json $cid darkside-image
buildah rm ${cid}
output=$(buildah inspect --type image darkside-image)
[ $(output | grep "u003" | wc -l) -eq 0 ]
output=$(buildah inspect --type image darkside-image | grep "u003" | wc -l)
[ "$output" -ne 0 ]
buildah rmi darkside-image
}

12
tests/rm.bats Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bats
load helpers
@test "remove multiple containers errors" {
run buildah --debug=false rm mycontainer1 mycontainer2 mycontainer3
[ "${lines[0]}" == "error removing container \"mycontainer1\": error reading build container: container not known" ]
[ "${lines[1]}" == "error removing container \"mycontainer2\": error reading build container: container not known" ]
[ "${lines[2]}" == "error removing container \"mycontainer3\": error reading build container: container not known" ]
[ $(wc -l <<< "$output") -eq 3 ]
[ "${status}" -eq 1 ]
}

80
tests/rmi.bats Normal file
View File

@@ -0,0 +1,80 @@
#!/usr/bin/env bats
load helpers
@test "remove one image" {
cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json alpine)
buildah rm "$cid"
buildah rmi alpine
run buildah --debug=false images -q
[ "$output" == "" ]
}
@test "remove multiple images" {
cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine)
cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox)
run buildah rmi alpine busybox
[ "$status" -eq 1 ]
run buildah --debug=false images -q
[ "$output" != "" ]
buildah rmi -f alpine busybox
run buildah --debug=false images -q
[ "$output" == "" ]
}
@test "remove all images" {
cid1=$(buildah from --signature-policy ${TESTSDIR}/policy.json scratch)
cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine)
cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox)
buildah rmi -a -f
run buildah --debug=false images -q
[ "$output" == "" ]
cid1=$(buildah from --signature-policy ${TESTSDIR}/policy.json scratch)
cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine)
cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox)
run buildah rmi --all
[ "$status" -eq 1 ]
run buildah --debug=false images -q
[ "$output" != "" ]
buildah rmi --all --force
run buildah --debug=false images -q
[ "$output" == "" ]
}
@test "use prune to remove dangling images" {
createrandom ${TESTDIR}/randomfile
createrandom ${TESTDIR}/other-randomfile
cid=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox)
run buildah --debug=false images -q
[ $(wc -l <<< "$output") -eq 1 ]
root=$(buildah mount $cid)
cp ${TESTDIR}/randomfile $root/randomfile
buildah unmount $cid
buildah commit --signature-policy ${TESTSDIR}/policy.json $cid containers-storage:new-image
run buildah --debug=false images -q
[ $(wc -l <<< "$output") -eq 2 ]
root=$(buildah mount $cid)
cp ${TESTDIR}/other-randomfile $root/other-randomfile
buildah unmount $cid
buildah commit --signature-policy ${TESTSDIR}/policy.json $cid containers-storage:new-image
run buildah --debug=false images -q
[ $(wc -l <<< "$output") -eq 3 ]
buildah rmi --prune
run buildah --debug=false images -q
[ $(wc -l <<< "$output") -eq 2 ]
buildah rmi --all --force
run buildah --debug=false images -q
[ "$output" == "" ]
}

View File

@@ -27,7 +27,7 @@ load helpers
buildah --debug=false run $cid -- rpmbuild --define "_topdir /rpmbuild" -ba /rpmbuild/SPECS/buildah.spec
# Build a second new container.
cid2=$(buildah --debug=false from --pull --signature-policy ${TESTSDIR}/policy.json registry.fedoraproject.org/fedora:26)
cid2=$(buildah --debug=false from --pull --signature-policy ${TESTSDIR}/policy.json registry.fedoraproject.org/fedora:27)
root2=$(buildah --debug=false mount $cid2)
# Copy the binary packages from the first container to the second one, and build a list of

View File

@@ -97,22 +97,27 @@ buildah images
docker logout localhost:5000
########
# Push using only certs, this should fail.
# Push using only certs, this should FAIL.
########
buildah push --cert-dir /root/auth --tls-verify=true alpine docker://localhost:5000/my-alpine
########
# Push using creds, certs and no transport, this should work.
# Push using creds, certs and no transport (docker://), this should work.
########
buildah push --cert-dir ~/auth --tls-verify=true --creds=testuser:testpassword alpine localhost:5000/my-alpine
########
# No creds anywhere, only the certificate, this should fail.
# Push using a bad password , this should FAIL.
########
buildah push --cert-dir ~/auth --tls-verify=true --creds=testuser:badpassword alpine localhost:5000/my-alpine
########
# No creds anywhere, only the certificate, this should FAIL.
########
buildah from localhost:5000/my-alpine --cert-dir /root/auth --tls-verify=true
########
# Log in with creds, this should work
# From with creds and certs, this should work
########
ctrid=$(buildah from localhost:5000/my-alpine --cert-dir /root/auth --tls-verify=true --creds=testuser:testpassword)
@@ -154,7 +159,7 @@ buildah images
########
########
# No credentials, this should fail.
# No credentials, this should FAIL.
########
buildah commit --cert-dir /root/auth --tls-verify=true alpine-working-container docker://localhost:5000/my-commit-alpine
@@ -163,10 +168,51 @@ buildah commit --cert-dir /root/auth --tls-verify=true alpine-working-container
########
buildah commit --cert-dir /root/auth --tls-verify=true --creds=testuser:testpassword alpine-working-container docker://localhost:5000/my-commit-alpine
########
# Use bad password on from/pull, this should FAIL
########
buildah from localhost:5000/my-commit-alpine --pull-always --cert-dir /root/auth --tls-verify=true --creds=testuser:badpassword
########
# Pull the new image that we just commited
########
buildah from localhost:5000/my-commit-alpine --cert-dir /root/auth --tls-verify=true --creds=testuser:testpassword
buildah from localhost:5000/my-commit-alpine --pull-always --cert-dir /root/auth --tls-verify=true --creds=testuser:testpassword
########
# Show stuff
########
docker ps --all
docker images
buildah containers
buildah images
########
# Create Dockerfile
########
FILE=./Dockerfile
/bin/cat <<EOM >$FILE
FROM localhost:5000/my-commit-alpine
EOM
chmod +x $FILE
########
# Clean up Buildah
########
buildah rm --all
buildah rmi -f $(buildah --debug=false images -q)
########
# Try Buildah bud with creds but no auth, this should FAIL
########
buildah bud -f ./Dockerfile --tls-verify=true --creds=testuser:testpassword
########
# Try Buildah bud with creds and auth, this should work
########
buildah bud -f ./Dockerfile --cert-dir /root/auth --tls-verify=true --creds=testuser:testpassword
########
# Show stuff
@@ -182,6 +228,9 @@ buildah images
########
# Clean up
########
read -p "Press enter to continue and clean up all"
rm -f ./Dockerfile
rm -rf ${TESTDIR}/auth
docker rm -f $(docker ps --all -q)
docker rmi -f $(docker images -q)

View File

@@ -11,6 +11,7 @@ exec gometalinter.v1 \
--enable-gc \
--exclude='error return value not checked.*(Close|Log|Print).*\(errcheck\)$' \
--exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$' \
--exclude='declaration of.*err.*shadows declaration.*\(vetshadow\)$'\
--exclude='duplicate of.*_test.go.*\(dupl\)$' \
--exclude='vendor\/.*' \
--disable=gotype \

View File

@@ -1,6 +1,8 @@
package util
import (
"fmt"
"net/url"
"path"
"strings"
@@ -9,8 +11,10 @@ import (
is "github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/docker/distribution/registry/api/errcode"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
const (
@@ -152,3 +156,23 @@ func AddImageNames(store storage.Store, image *storage.Image, addNames []string)
}
return nil
}
// GetFailureCause checks the type of the error "err" and returns a new
// error message that reflects the reason of the failure.
// In case err type is not a familiar one the error "defaultError" is returned.
func GetFailureCause(err, defaultError error) error {
switch nErr := errors.Cause(err).(type) {
case errcode.Errors:
return cli.NewMultiError([]error(nErr)...)
case errcode.Error, *url.Error:
return nErr
default:
// HACK: In case the error contains "not authorized" like in
// https://github.com/containers/image/blob/master/docker/docker_image_dest.go#L193-L205
// TODO(bshuster): change "containers/images" to return "errcode" rather than "error".
if strings.Contains(nErr.Error(), "not authorized") {
return fmt.Errorf("unauthorized: authentication required")
}
return defaultError
}
}

View File

@@ -54,3 +54,4 @@ github.com/containerd/continuity master
github.com/gogo/protobuf master
github.com/xeipuuv/gojsonpointer master
github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
github.com/projectatomic/libpod master

View File

@@ -70,7 +70,7 @@ func newImageDestination(ref dirReference, compress bool) (types.ImageDestinatio
}
}
// create version file
err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0755)
err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0644)
if err != nil {
return nil, errors.Wrapf(err, "error creating version file %q", d.ref.versionPath())
}

View File

@@ -8,7 +8,10 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -24,10 +27,9 @@ import (
)
const (
dockerHostname = "docker.io"
dockerRegistry = "registry-1.docker.io"
systemPerHostCertDirPath = "/etc/docker/certs.d"
dockerHostname = "docker.io"
dockerV1Hostname = "index.docker.io"
dockerRegistry = "registry-1.docker.io"
resolvedPingV2URL = "%s://%s/v2/"
resolvedPingV1URL = "%s://%s/v1/_ping"
@@ -49,6 +51,7 @@ var (
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
// ErrUnauthorizedForCredentials is returned when the status code returned is 401
ErrUnauthorizedForCredentials = errors.New("unable to retrieve auth token: invalid username/password")
systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
)
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
@@ -66,9 +69,10 @@ type extensionSignatureList struct {
}
type bearerToken struct {
Token string `json:"token"`
ExpiresIn int `json:"expires_in"`
IssuedAt time.Time `json:"issued_at"`
Token string `json:"token"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
IssuedAt time.Time `json:"issued_at"`
}
// dockerClient is configuration for dealing with a single Docker registry.
@@ -96,6 +100,24 @@ type authScope struct {
actions string
}
func newBearerTokenFromJSONBlob(blob []byte) (*bearerToken, error) {
token := new(bearerToken)
if err := json.Unmarshal(blob, &token); err != nil {
return nil, err
}
if token.Token == "" {
token.Token = token.AccessToken
}
if token.ExpiresIn < minimumTokenLifetimeSeconds {
token.ExpiresIn = minimumTokenLifetimeSeconds
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
}
if token.IssuedAt.IsZero() {
token.IssuedAt = time.Now().UTC()
}
return token, nil
}
// this is cloned from docker/go-connections because upstream docker has changed
// it and make deps here fails otherwise.
// We'll drop this once we upgrade to docker 1.13.x deps.
@@ -109,19 +131,42 @@ func serverDefault() *tls.Config {
}
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
func dockerCertDir(ctx *types.SystemContext, hostPort string) (string, error) {
if ctx != nil && ctx.DockerCertPath != "" {
return ctx.DockerCertPath
return ctx.DockerCertPath, nil
}
var hostCertDir string
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
hostCertDir = ctx.DockerPerHostCertDirPath
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
} else {
hostCertDir = systemPerHostCertDirPath
return filepath.Join(ctx.DockerPerHostCertDirPath, hostPort), nil
}
return filepath.Join(hostCertDir, hostPort)
var (
hostCertDir string
fullCertDirPath string
)
for _, systemPerHostCertDirPath := range systemPerHostCertDirPaths {
if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
} else {
hostCertDir = systemPerHostCertDirPath
}
fullCertDirPath = filepath.Join(hostCertDir, hostPort)
_, err := os.Stat(fullCertDirPath)
if err == nil {
break
}
if os.IsNotExist(err) {
continue
}
if os.IsPermission(err) {
logrus.Debugf("error accessing certs directory due to permissions: %v", err)
continue
}
if err != nil {
return "", err
}
}
return fullCertDirPath, nil
}
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
@@ -155,7 +200,10 @@ func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, pa
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
// generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
// undocumented and may change if docker/docker changes.
certDir := dockerCertDir(ctx, hostName)
certDir, err := dockerCertDir(ctx, hostName)
if err != nil {
return nil, err
}
if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil {
return nil, err
}
@@ -202,6 +250,100 @@ func CheckAuth(ctx context.Context, sCtx *types.SystemContext, username, passwor
}
}
// SearchResult holds the information of each matching image
// It matches the output returned by the v1 endpoint
type SearchResult struct {
Name string `json:"name"`
Description string `json:"description"`
// StarCount states the number of stars the image has
StarCount int `json:"star_count"`
IsTrusted bool `json:"is_trusted"`
// IsAutomated states whether the image is an automated build
IsAutomated bool `json:"is_automated"`
// IsOfficial states whether the image is an official build
IsOfficial bool `json:"is_official"`
}
// SearchRegistry queries a registry for images that contain "image" in their name
// The limit is the max number of results desired
// Note: The limit value doesn't work with all registries
// for example registry.access.redhat.com returns all the results without limiting it to the limit value
func SearchRegistry(ctx context.Context, sCtx *types.SystemContext, registry, image string, limit int) ([]SearchResult, error) {
type V2Results struct {
// Repositories holds the results returned by the /v2/_catalog endpoint
Repositories []string `json:"repositories"`
}
type V1Results struct {
// Results holds the results returned by the /v1/search endpoint
Results []SearchResult `json:"results"`
}
v2Res := &V2Results{}
v1Res := &V1Results{}
// The /v2/_catalog endpoint has been disabled for docker.io therefore the call made to that endpoint will fail
// So using the v1 hostname for docker.io for simplicity of implementation and the fact that it returns search results
if registry == dockerHostname {
registry = dockerV1Hostname
}
client, err := newDockerClientWithDetails(sCtx, registry, "", "", "", nil, "")
if err != nil {
return nil, errors.Wrapf(err, "error creating new docker client")
}
logrus.Debugf("trying to talk to v2 search endpoint\n")
resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil)
if err != nil {
logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
logrus.Debugf("error getting search results from v2 endpoint %q, status code %q", registry, resp.StatusCode)
} else {
if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
return nil, err
}
searchRes := []SearchResult{}
for _, repo := range v2Res.Repositories {
if strings.Contains(repo, image) {
res := SearchResult{
Name: repo,
}
searchRes = append(searchRes, res)
}
}
return searchRes, nil
}
}
// set up the query values for the v1 endpoint
u := url.URL{
Path: "/v1/search",
}
q := u.Query()
q.Set("q", image)
q.Set("n", strconv.Itoa(limit))
u.RawQuery = q.Encode()
logrus.Debugf("trying to talk to v1 search endpoint\n")
resp, err = client.makeRequest(ctx, "GET", u.String(), nil, nil)
if err != nil {
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
logrus.Debugf("error getting search results from v1 endpoint %q, status code %q", registry, resp.StatusCode)
} else {
if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
return nil, err
}
return v1Res.Results, nil
}
}
return nil, errors.Wrapf(err, "couldn't search registry %q", registry)
}
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/.
func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
@@ -332,18 +474,8 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope
if err != nil {
return nil, err
}
var token bearerToken
if err := json.Unmarshal(tokenBlob, &token); err != nil {
return nil, err
}
if token.ExpiresIn < minimumTokenLifetimeSeconds {
token.ExpiresIn = minimumTokenLifetimeSeconds
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
}
if token.IssuedAt.IsZero() {
token.IssuedAt = time.Now().UTC()
}
return &token, nil
return newBearerTokenFromJSONBlob(tokenBlob)
}
// detectProperties detects various properties of the registry.

View File

@@ -131,7 +131,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
defer res.Body.Close()
if res.StatusCode != http.StatusAccepted {
logrus.Debugf("Error initiating layer upload, response %#v", *res)
return types.BlobInfo{}, errors.Errorf("Error initiating layer upload to %s, status %d", uploadPath, res.StatusCode)
return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error initiating layer upload to %s", uploadPath)
}
uploadLocation, err := res.Location()
if err != nil {
@@ -167,7 +167,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
logrus.Debugf("Error uploading layer, response %#v", *res)
return types.BlobInfo{}, errors.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode)
return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer to %s", uploadLocation)
}
logrus.Debugf("Upload of layer %s complete", computedDigest)
@@ -196,7 +196,7 @@ func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, erro
return true, getBlobSize(res), nil
case http.StatusUnauthorized:
logrus.Debugf("... not authorized")
return false, -1, errors.Errorf("not authorized to read from destination repository %s", reference.Path(d.ref.ref))
return false, -1, client.HandleErrorResponse(res)
case http.StatusNotFound:
logrus.Debugf("... not present")
return false, -1, nil
@@ -447,7 +447,7 @@ sigExists:
logrus.Debugf("Error body %s", string(body))
}
logrus.Debugf("Error uploading signature, status %d, %#v", res.StatusCode, res)
return errors.Errorf("Error uploading signature to %s, status %d", path, res.StatusCode)
return errors.Wrapf(client.HandleErrorResponse(res), "Error uploading signature to %s", path)
}
}

View File

@@ -149,6 +149,16 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
switch options.ManifestMIMEType {
case "": // No conversion, OK
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
// We can't directly convert to V1, but we can transitively convert via a V2 image
m2, err := copy.convertToManifestSchema2()
if err != nil {
return nil, err
}
return m2.UpdatedImage(types.ManifestUpdateOptions{
ManifestMIMEType: options.ManifestMIMEType,
InformationOnly: options.InformationOnly,
})
case manifest.DockerV2Schema2MediaType:
return copy.convertToManifestSchema2()
default:

View File

@@ -14,6 +14,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"unsafe"
@@ -175,7 +176,10 @@ func fixFiles(selinuxHnd *C.struct_selabel_handle, root string, dir string, user
if err != nil {
return err
}
relPath = fmt.Sprintf("/%s", relPath)
// Handle /exports/hostfs as a special case. Files under this directory are copied to the host,
// thus we benefit from maintaining the same SELinux label they would have on the host as we could
// use hard links instead of copying the files.
relPath = fmt.Sprintf("/%s", strings.TrimPrefix(relPath, "exports/hostfs/"))
relPathC := C.CString(relPath)
defer C.free(unsafe.Pointer(relPathC))
@@ -237,7 +241,7 @@ func generateTarSplitMetadata(output *bytes.Buffer, file string) error {
}
defer stream.Close()
gzReader, err := gzip.NewReader(stream)
gzReader, err := archive.DecompressStream(stream)
if err != nil {
return err
}
@@ -383,7 +387,7 @@ func (d *ostreeImageDestination) Commit() error {
var selinuxHnd *C.struct_selabel_handle
if os.Getuid() == 0 && selinux.GetEnabled() {
selinuxHnd, err := C.selabel_open(C.SELABEL_CTX_FILE, nil, 0)
selinuxHnd, err = C.selabel_open(C.SELABEL_CTX_FILE, nil, 0)
if selinuxHnd == nil {
return errors.Wrapf(err, "cannot open the SELinux DB")
}

View File

@@ -89,5 +89,5 @@ func (r *tarballReference) DeleteImage(ctx *types.SystemContext) error {
}
func (r *tarballReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
return nil, fmt.Errorf("destination not implemented yet")
return nil, fmt.Errorf(`"tarball:" locations can only be read from, not written to`)
}

View File

@@ -463,9 +463,9 @@ func (a *Driver) isParent(id, parent string) bool {
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
func (a *Driver) Diff(id, parent string) (io.ReadCloser, error) {
func (a *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
if !a.isParent(id, parent) {
return a.naiveDiff.Diff(id, parent)
return a.naiveDiff.Diff(id, parent, mountLabel)
}
// AUFS doesn't need the parent layer to produce a diff.
@@ -502,9 +502,9 @@ func (a *Driver) applyDiff(id string, diff io.Reader) error {
// DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func (a *Driver) DiffSize(id, parent string) (size int64, err error) {
func (a *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) {
if !a.isParent(id, parent) {
return a.naiveDiff.DiffSize(id, parent)
return a.naiveDiff.DiffSize(id, parent, mountLabel)
}
// AUFS doesn't need the parent layer to calculate the diff size.
return directory.Size(path.Join(a.rootPath(), "diff", id))
@@ -513,9 +513,9 @@ func (a *Driver) DiffSize(id, parent string) (size int64, err error) {
// ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the
// new layer in bytes.
func (a *Driver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) {
func (a *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) {
if !a.isParent(id, parent) {
return a.naiveDiff.ApplyDiff(id, parent, diff)
return a.naiveDiff.ApplyDiff(id, parent, mountLabel, diff)
}
// AUFS doesn't need the parent id to apply the diff if it is the direct parent.
@@ -523,14 +523,14 @@ func (a *Driver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err e
return
}
return a.DiffSize(id, parent)
return a.DiffSize(id, parent, mountLabel)
}
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
func (a *Driver) Changes(id, parent string) ([]archive.Change, error) {
func (a *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) {
if !a.isParent(id, parent) {
return a.naiveDiff.Changes(id, parent)
return a.naiveDiff.Changes(id, parent, mountLabel)
}
// AUFS doesn't have snapshots, so we need to get changes from all parent

View File

@@ -92,19 +92,19 @@ type ProtoDriver interface {
type DiffDriver interface {
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
Diff(id, parent string) (io.ReadCloser, error)
Diff(id, parent, mountLabel string) (io.ReadCloser, error)
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
Changes(id, parent string) ([]archive.Change, error)
Changes(id, parent, mountLabel string) ([]archive.Change, error)
// ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the
// new layer in bytes.
// The io.Reader must be an uncompressed stream.
ApplyDiff(id, parent string, diff io.Reader) (size int64, err error)
ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error)
// DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
DiffSize(id, parent string) (size int64, err error)
DiffSize(id, parent, mountLabel string) (size int64, err error)
}
// Driver is the interface for layered/snapshot file system drivers.

View File

@@ -31,10 +31,10 @@ type NaiveDiffDriver struct {
// NewNaiveDiffDriver returns a fully functional driver that wraps the
// given ProtoDriver and adds the capability of the following methods which
// it may or may not support on its own:
// Diff(id, parent string) (io.ReadCloser, error)
// Changes(id, parent string) ([]archive.Change, error)
// ApplyDiff(id, parent string, diff io.Reader) (size int64, err error)
// DiffSize(id, parent string) (size int64, err error)
// Diff(id, parent, mountLabel string) (io.ReadCloser, error)
// Changes(id, parent, mountLabel string) ([]archive.Change, error)
// ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error)
// DiffSize(id, parent, mountLabel string) (size int64, err error)
func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Driver {
return &NaiveDiffDriver{ProtoDriver: driver,
uidMaps: uidMaps,
@@ -43,11 +43,11 @@ func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Dr
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err error) {
func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadCloser, err error) {
startTime := time.Now()
driver := gdw.ProtoDriver
layerFs, err := driver.Get(id, "")
layerFs, err := driver.Get(id, mountLabel)
if err != nil {
return nil, err
}
@@ -70,7 +70,7 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err
}), nil
}
parentFs, err := driver.Get(parent, "")
parentFs, err := driver.Get(parent, mountLabel)
if err != nil {
return nil, err
}
@@ -101,10 +101,10 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
func (gdw *NaiveDiffDriver) Changes(id, parent, mountLabel string) ([]archive.Change, error) {
driver := gdw.ProtoDriver
layerFs, err := driver.Get(id, "")
layerFs, err := driver.Get(id, mountLabel)
if err != nil {
return nil, err
}
@@ -113,7 +113,7 @@ func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error)
parentFs := ""
if parent != "" {
parentFs, err = driver.Get(parent, "")
parentFs, err = driver.Get(parent, mountLabel)
if err != nil {
return nil, err
}
@@ -126,11 +126,11 @@ func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error)
// ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the
// new layer in bytes.
func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) {
func (gdw *NaiveDiffDriver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) {
driver := gdw.ProtoDriver
// Mount the root filesystem so we can apply the diff/layer.
layerFs, err := driver.Get(id, "")
layerFs, err := driver.Get(id, mountLabel)
if err != nil {
return
}
@@ -151,15 +151,15 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size i
// DiffSize calculates the changes between the specified layer
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
func (gdw *NaiveDiffDriver) DiffSize(id, parent, mountLabel string) (size int64, err error) {
driver := gdw.ProtoDriver
changes, err := gdw.Changes(id, parent)
changes, err := gdw.Changes(id, parent, mountLabel)
if err != nil {
return
}
layerFs, err := driver.Get(id, "")
layerFs, err := driver.Get(id, mountLabel)
if err != nil {
return
}

View File

@@ -245,9 +245,7 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
return false, err
}
if !supportsDType {
logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
// TODO: Will make fatal when CRI-O Has AMI built on RHEL7.4
// return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs)
return false, overlayutils.ErrDTypeNotSupported("overlay", backingFs)
}
// Try a test mount in the specific location we're looking at using.
@@ -701,9 +699,9 @@ func (d *Driver) isParent(id, parent string) bool {
}
// ApplyDiff applies the new layer into a root
func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) {
func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) {
if !d.isParent(id, parent) {
return d.naiveDiff.ApplyDiff(id, parent, diff)
return d.naiveDiff.ApplyDiff(id, parent, mountLabel, diff)
}
applyDir := d.getDiffPath(id)
@@ -730,18 +728,18 @@ func (d *Driver) getDiffPath(id string) string {
// DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
func (d *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.DiffSize(id, parent)
return d.naiveDiff.DiffSize(id, parent, mountLabel)
}
return directory.Size(d.getDiffPath(id))
}
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.Diff(id, parent)
return d.naiveDiff.Diff(id, parent, mountLabel)
}
diffPath := d.getDiffPath(id)
@@ -756,9 +754,9 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.Changes(id, parent)
return d.naiveDiff.Changes(id, parent, mountLabel)
}
// Overlay doesn't have snapshots, so we need to get changes from all parent
// layers.

View File

@@ -472,7 +472,7 @@ func (d *Driver) Cleanup() error {
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
// The layer should be mounted when calling this function
func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
func (d *Driver) Diff(id, parent, mountLabel string) (_ io.ReadCloser, err error) {
panicIfUsedByLcow()
rID, err := d.resolveID(id)
if err != nil {
@@ -509,7 +509,7 @@ func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
// The layer should not be mounted when calling this function.
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) {
panicIfUsedByLcow()
rID, err := d.resolveID(id)
if err != nil {
@@ -565,7 +565,7 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
// layer with the specified id and parent, returning the size of the
// new layer in bytes.
// The layer should not be mounted when calling this function
func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (int64, error) {
panicIfUsedByLcow()
var layerChain []string
if parent != "" {
@@ -600,14 +600,14 @@ func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
// DiffSize calculates the changes between the specified layer
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
func (d *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) {
panicIfUsedByLcow()
rPId, err := d.resolveID(parent)
if err != nil {
return
}
changes, err := d.Changes(id, rPId)
changes, err := d.Changes(id, rPId, mountLabel)
if err != nil {
return
}

View File

@@ -778,11 +778,11 @@ func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID st
}
func (r *layerStore) Changes(from, to string) ([]archive.Change, error) {
from, to, _, err := r.findParentAndLayer(from, to)
from, to, toLayer, err := r.findParentAndLayer(from, to)
if err != nil {
return nil, ErrLayerUnknown
}
return r.driver.Changes(to, from)
return r.driver.Changes(to, from, toLayer.MountLabel)
}
type simpleGetCloser struct {
@@ -855,7 +855,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
}
if from != toLayer.Parent {
diff, err := r.driver.Diff(to, from)
diff, err := r.driver.Diff(to, from, toLayer.MountLabel)
if err != nil {
return nil, err
}
@@ -867,7 +867,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
if !os.IsNotExist(err) {
return nil, err
}
diff, err := r.driver.Diff(to, from)
diff, err := r.driver.Diff(to, from, toLayer.MountLabel)
if err != nil {
return nil, err
}
@@ -906,11 +906,12 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
}
func (r *layerStore) DiffSize(from, to string) (size int64, err error) {
from, to, _, err = r.findParentAndLayer(from, to)
var toLayer *Layer
from, to, toLayer, err = r.findParentAndLayer(from, to)
if err != nil {
return -1, ErrLayerUnknown
}
return r.driver.DiffSize(to, from)
return r.driver.DiffSize(to, from, toLayer.MountLabel)
}
func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error) {
@@ -950,7 +951,7 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error
if err != nil {
return -1, err
}
size, err = r.driver.ApplyDiff(layer.ID, layer.Parent, payload)
size, err = r.driver.ApplyDiff(layer.ID, layer.Parent, layer.MountLabel, payload)
if err != nil {
return -1, err
}

View File

@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: layers.go
// source: layers.go. Hack to make this work on github.com
package storage

View File

@@ -2,14 +2,11 @@ package storage
import (
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// A Locker represents a file lock where the file is used to cache an
@@ -33,16 +30,8 @@ type Locker interface {
IsReadWrite() bool
}
type lockfile struct {
mu sync.Mutex
file string
fd uintptr
lw string
locktype int16
}
var (
lockfiles map[string]*lockfile
lockfiles map[string]Locker
lockfilesLock sync.Mutex
)
@@ -52,7 +41,7 @@ func GetLockfile(path string) (Locker, error) {
lockfilesLock.Lock()
defer lockfilesLock.Unlock()
if lockfiles == nil {
lockfiles = make(map[string]*lockfile)
lockfiles = make(map[string]Locker)
}
cleanPath := filepath.Clean(path)
if locker, ok := lockfiles[cleanPath]; ok {
@@ -61,12 +50,10 @@ func GetLockfile(path string) (Locker, error) {
}
return locker, nil
}
fd, err := unix.Open(cleanPath, os.O_RDWR|os.O_CREATE, unix.S_IRUSR|unix.S_IWUSR)
locker, err := getLockFile(path, false) // platform dependent locker
if err != nil {
return nil, errors.Wrapf(err, "error opening %q", cleanPath)
return nil, err
}
unix.CloseOnExec(fd)
locker := &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_WRLCK}
lockfiles[filepath.Clean(path)] = locker
return locker, nil
}
@@ -77,7 +64,7 @@ func GetROLockfile(path string) (Locker, error) {
lockfilesLock.Lock()
defer lockfilesLock.Unlock()
if lockfiles == nil {
lockfiles = make(map[string]*lockfile)
lockfiles = make(map[string]Locker)
}
cleanPath := filepath.Clean(path)
if locker, ok := lockfiles[cleanPath]; ok {
@@ -86,99 +73,10 @@ func GetROLockfile(path string) (Locker, error) {
}
return locker, nil
}
fd, err := unix.Open(cleanPath, os.O_RDONLY, 0)
locker, err := getLockFile(path, true) // platform dependent locker
if err != nil {
return nil, errors.Wrapf(err, "error opening %q", cleanPath)
return nil, err
}
unix.CloseOnExec(fd)
locker := &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_RDLCK}
lockfiles[filepath.Clean(path)] = locker
return locker, nil
}
// Lock locks the lock file
func (l *lockfile) Lock() {
lk := unix.Flock_t{
Type: l.locktype,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0,
Pid: int32(os.Getpid()),
}
l.mu.Lock()
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
time.Sleep(10 * time.Millisecond)
}
}
// Unlock unlocks the lock file
func (l *lockfile) Unlock() {
lk := unix.Flock_t{
Type: unix.F_UNLCK,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0,
Pid: int32(os.Getpid()),
}
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
time.Sleep(10 * time.Millisecond)
}
l.mu.Unlock()
}
// Touch updates the lock file with the UID of the user
func (l *lockfile) Touch() error {
l.lw = stringid.GenerateRandomID()
id := []byte(l.lw)
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
if err != nil {
return err
}
n, err := unix.Write(int(l.fd), id)
if err != nil {
return err
}
if n != len(id) {
return unix.ENOSPC
}
err = unix.Fsync(int(l.fd))
if err != nil {
return err
}
return nil
}
// Modified indicates if the lock file has been updated since the last time it was loaded
func (l *lockfile) Modified() (bool, error) {
id := []byte(l.lw)
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
if err != nil {
return true, err
}
n, err := unix.Read(int(l.fd), id)
if err != nil {
return true, err
}
if n != len(id) {
return true, unix.ENOSPC
}
lw := l.lw
l.lw = string(id)
return l.lw != lw, nil
}
// TouchedSince indicates if the lock file has been touched since the specified time
func (l *lockfile) TouchedSince(when time.Time) bool {
st := unix.Stat_t{}
err := unix.Fstat(int(l.fd), &st)
if err != nil {
return true
}
touched := time.Unix(statTMtimeUnix(st))
return when.Before(touched)
}
// IsRWLock indicates if the lock file is a read-write lock
func (l *lockfile) IsReadWrite() bool {
return (l.locktype == unix.F_WRLCK)
}

View File

@@ -0,0 +1,19 @@
// +build darwin freebsd
package storage
import (
"time"
"golang.org/x/sys/unix"
)
func (l *lockfile) TouchedSince(when time.Time) bool {
st := unix.Stat_t{}
err := unix.Fstat(int(l.fd), &st)
if err != nil {
return true
}
touched := time.Unix(st.Mtimespec.Unix())
return when.Before(touched)
}

20
vendor/github.com/containers/storage/lockfile_linux.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
// +build linux solaris
package storage
import (
"time"
"golang.org/x/sys/unix"
)
// TouchedSince indicates if the lock file has been touched since the specified time
func (l *lockfile) TouchedSince(when time.Time) bool {
st := unix.Stat_t{}
err := unix.Fstat(int(l.fd), &st)
if err != nil {
return true
}
touched := time.Unix(st.Mtim.Unix())
return when.Before(touched)
}

115
vendor/github.com/containers/storage/lockfile_unix.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
// +build linux solaris darwin freebsd
package storage
import (
"os"
"sync"
"time"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func getLockFile(path string, ro bool) (Locker, error) {
var fd int
var err error
if ro {
fd, err = unix.Open(path, os.O_RDONLY, 0)
} else {
fd, err = unix.Open(path, os.O_RDWR|os.O_CREATE, unix.S_IRUSR|unix.S_IWUSR)
}
if err != nil {
return nil, errors.Wrapf(err, "error opening %q", path)
}
unix.CloseOnExec(fd)
if ro {
return &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_RDLCK}, nil
}
return &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_WRLCK}, nil
}
type lockfile struct {
mu sync.Mutex
file string
fd uintptr
lw string
locktype int16
}
// Lock locks the lock file
func (l *lockfile) Lock() {
lk := unix.Flock_t{
Type: l.locktype,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0,
Pid: int32(os.Getpid()),
}
l.mu.Lock()
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
time.Sleep(10 * time.Millisecond)
}
}
// Unlock unlocks the lock file
func (l *lockfile) Unlock() {
lk := unix.Flock_t{
Type: unix.F_UNLCK,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0,
Pid: int32(os.Getpid()),
}
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
time.Sleep(10 * time.Millisecond)
}
l.mu.Unlock()
}
// Touch updates the lock file with the UID of the user
func (l *lockfile) Touch() error {
l.lw = stringid.GenerateRandomID()
id := []byte(l.lw)
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
if err != nil {
return err
}
n, err := unix.Write(int(l.fd), id)
if err != nil {
return err
}
if n != len(id) {
return unix.ENOSPC
}
err = unix.Fsync(int(l.fd))
if err != nil {
return err
}
return nil
}
// Modified indicates if the lock file has been updated since the last time it was loaded
func (l *lockfile) Modified() (bool, error) {
id := []byte(l.lw)
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
if err != nil {
return true, err
}
n, err := unix.Read(int(l.fd), id)
if err != nil {
return true, err
}
if n != len(id) {
return true, unix.ENOSPC
}
lw := l.lw
l.lw = string(id)
return l.lw != lw, nil
}
// IsRWLock indicates if the lock file is a read-write lock
func (l *lockfile) IsReadWrite() bool {
return (l.locktype == unix.F_WRLCK)
}

View File

@@ -0,0 +1,40 @@
// +build windows
package storage
import (
"os"
"sync"
"time"
)
func getLockFile(path string, ro bool) (Locker, error) {
return &lockfile{}, nil
}
type lockfile struct {
mu sync.Mutex
file string
}
func (l *lockfile) Lock() {
}
func (l *lockfile) Unlock() {
}
func (l *lockfile) Modified() (bool, error) {
return false, nil
}
func (l *lockfile) Touch() error {
return nil
}
func (l *lockfile) IsReadWrite() bool {
return false
}
func (l *lockfile) TouchedSince(when time.Time) bool {
stat, err := os.Stat(l.file)
if err != nil {
return true
}
return when.Before(stat.ModTime())
}

View File

@@ -0,0 +1,97 @@
// +build ignore
// Simple tool to create an archive stream from an old and new directory
//
// By default it will stream the comparison of two temporary directories with junk files
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
)
var (
flDebug = flag.Bool("D", false, "debugging output")
flNewDir = flag.String("newdir", "", "")
flOldDir = flag.String("olddir", "", "")
log = logrus.New()
)
func main() {
flag.Usage = func() {
fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)")
fmt.Printf("%s [OPTIONS]\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
log.Out = os.Stderr
if (len(os.Getenv("DEBUG")) > 0) || *flDebug {
logrus.SetLevel(logrus.DebugLevel)
}
var newDir, oldDir string
if len(*flNewDir) == 0 {
var err error
newDir, err = ioutil.TempDir("", "storage-test-newDir")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(newDir)
if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil {
log.Fatal(err)
}
} else {
newDir = *flNewDir
}
if len(*flOldDir) == 0 {
oldDir, err := ioutil.TempDir("", "storage-test-oldDir")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(oldDir)
} else {
oldDir = *flOldDir
}
changes, err := archive.ChangesDirs(newDir, oldDir)
if err != nil {
log.Fatal(err)
}
a, err := archive.ExportChanges(newDir, changes)
if err != nil {
log.Fatal(err)
}
defer a.Close()
i, err := io.Copy(os.Stdout, a)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i)
}
func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
fileData := []byte("fooo")
for n := 0; n < numberOfFiles; n++ {
fileName := fmt.Sprintf("file-%d", n)
if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
return 0, err
}
if makeLinks {
if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
return 0, err
}
}
}
totalSize := numberOfFiles * len(fileData)
return totalSize, nil
}

View File

@@ -77,10 +77,7 @@ func (idx *TruncIndex) addID(id string) error {
func (idx *TruncIndex) Add(id string) error {
idx.Lock()
defer idx.Unlock()
if err := idx.addID(id); err != nil {
return err
}
return nil
return idx.addID(id)
}
// Delete removes an ID from the TruncIndex. If there are multiple IDs
@@ -128,8 +125,13 @@ func (idx *TruncIndex) Get(s string) (string, error) {
return "", ErrNotExist
}
// Iterate iterates over all stored IDs, and passes each of them to the given handler.
// Iterate iterates over all stored IDs and passes each of them to the given
// handler. Take care that the handler method does not call any public
// method on truncindex as the internal locking is not reentrant/recursive
// and will result in deadlock.
func (idx *TruncIndex) Iterate(handler func(id string)) {
idx.Lock()
defer idx.Unlock()
idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
handler(string(prefix))
return nil

View File

@@ -1,11 +0,0 @@
// +build linux solaris
package storage
import (
"golang.org/x/sys/unix"
)
func statTMtimeUnix(st unix.Stat_t) (int64, int64) {
return st.Mtim.Unix()
}

View File

@@ -1,11 +0,0 @@
// +build !linux,!solaris
package storage
import (
"golang.org/x/sys/unix"
)
func statTMtimeUnix(st unix.Stat_t) (int64, int64) {
return st.Mtimespec.Unix()
}

View File

@@ -1,60 +0,0 @@
## About
This directory contains a collection of scripts used to build and manage this
repository. If there are any issues regarding the intention of a particular
script (or even part of a certain script), please reach out to us.
It may help us either refine our current scripts, or add on new ones
that are appropriate for a given use case.
## DinD (dind.sh)
DinD is a wrapper script which allows Docker to be run inside a Docker
container. DinD requires the container to
be run with privileged mode enabled.
## Generate Authors (generate-authors.sh)
Generates AUTHORS; a file with all the names and corresponding emails of
individual contributors. AUTHORS can be found in the home directory of
this repository.
## Make
There are two make files, each with different extensions. Neither are supposed
to be called directly; only invoke `make`. Both scripts run inside a Docker
container.
### make.ps1
- The Windows native build script that uses PowerShell semantics; it is limited
unlike `hack\make.sh` since it does not provide support for the full set of
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
does provide support for local Windows development and Windows to Windows CI.
More information is found within `make.ps1` by the author, @jhowardmsft
### make.sh
- Referenced via `make test` when running tests on a local machine,
or directly referenced when running tests inside a Docker development container.
- When running on a local machine, `make test` to run all tests found in
`test`, `test-unit`, `test-integration`, and `test-docker-py` on
your local machine. The default timeout is set in `make.sh` to 60 minutes
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
all of the tests.
- When running inside a Docker development container, `hack/make.sh` does
not have a single target that runs all the tests. You need to provide a
single command line with multiple targets that performs the same thing.
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration test-docker-py`
- For more information related to testing outside the scope of this README,
refer to
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
## Release (release.sh)
Releases any bundles built by `make` on a public AWS S3 bucket.
For information regarding configuration, please view `release.sh`.
## Vendor (vendor.sh)
A shell script that is a wrapper around Vndr. For information on how to use
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)

View File

@@ -1,69 +0,0 @@
# Integration Testing on Swarm
IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster
## Architecture
### Master service
- Works as a funker caller
- Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`)
### Worker service
- Works as a funker callee
- Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`)
### Client
- Controls master and workers via `docker stack`
- No need to have a local daemon
Typically, the master and workers are supposed to be running on a cloud environment,
while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows.
## Requirement
- Docker daemon 1.13 or later
- Private registry for distributed execution with multiple nodes
## Usage
### Step 1: Prepare images
$ make build-integration-cli-on-swarm
Following environment variables are known to work in this step:
- `BUILDFLAGS`
- `DOCKER_INCREMENTAL_BINARY`
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
### Step 2: Execute tests
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest
Following environment variables are known to work in this step:
- `DOCKER_GRAPHDRIVER`
- `DOCKER_EXPERIMENTAL`
#### Flags
Basic flags:
- `-replicas N`: the number of worker service replicas. i.e. degree of parallelism.
- `-chunks N`: the number of chunks. By default, `chunks` == `replicas`.
- `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`.
Experimental flags for mitigating makespan nonuniformity:
- `-shuffle`: Shuffle the test filter strings
Flags for debugging IT on Swarm itself:
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
- `-dry-run`: skip the actual workload
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm

View File

@@ -1,2 +0,0 @@
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773

View File

@@ -1,60 +0,0 @@
## About
This directory contains a collection of scripts used to build and manage this
repository. If there are any issues regarding the intention of a particular
script (or even part of a certain script), please reach out to us.
It may help us either refine our current scripts, or add on new ones
that are appropriate for a given use case.
## DinD (dind.sh)
DinD is a wrapper script which allows Docker to be run inside a Docker
container. DinD requires the container to
be run with privileged mode enabled.
## Generate Authors (generate-authors.sh)
Generates AUTHORS; a file with all the names and corresponding emails of
individual contributors. AUTHORS can be found in the home directory of
this repository.
## Make
There are two make files, each with different extensions. Neither are supposed
to be called directly; only invoke `make`. Both scripts run inside a Docker
container.
### make.ps1
- The Windows native build script that uses PowerShell semantics; it is limited
unlike `hack\make.sh` since it does not provide support for the full set of
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
does provide support for local Windows development and Windows to Windows CI.
More information is found within `make.ps1` by the author, @jhowardmsft
### make.sh
- Referenced via `make test` when running tests on a local machine,
or directly referenced when running tests inside a Docker development container.
- When running on a local machine, `make test` to run all tests found in
`test`, `test-unit`, `test-integration`, and `test-docker-py` on
your local machine. The default timeout is set in `make.sh` to 60 minutes
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
all of the tests.
- When running inside a Docker development container, `hack/make.sh` does
not have a single target that runs all the tests. You need to provide a
single command line with multiple targets that performs the same thing.
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration test-docker-py`
- For more information related to testing outside the scope of this README,
refer to
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
## Release (release.sh)
Releases any bundles built by `make` on a public AWS S3 bucket.
For information regarding configuration, please view `release.sh`.
## Vendor (vendor.sh)
A shell script that is a wrapper around Vndr. For information on how to use
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)

View File

@@ -1,69 +0,0 @@
# Integration Testing on Swarm
IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster
## Architecture
### Master service
- Works as a funker caller
- Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`)
### Worker service
- Works as a funker callee
- Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`)
### Client
- Controls master and workers via `docker stack`
- No need to have a local daemon
Typically, the master and workers are supposed to be running on a cloud environment,
while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows.
## Requirement
- Docker daemon 1.13 or later
- Private registry for distributed execution with multiple nodes
## Usage
### Step 1: Prepare images
$ make build-integration-cli-on-swarm
Following environment variables are known to work in this step:
- `BUILDFLAGS`
- `DOCKER_INCREMENTAL_BINARY`
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
### Step 2: Execute tests
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest
Following environment variables are known to work in this step:
- `DOCKER_GRAPHDRIVER`
- `DOCKER_EXPERIMENTAL`
#### Flags
Basic flags:
- `-replicas N`: the number of worker service replicas. i.e. degree of parallelism.
- `-chunks N`: the number of chunks. By default, `chunks` == `replicas`.
- `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`.
Experimental flags for mitigating makespan nonuniformity:
- `-shuffle`: Shuffle the test filter strings
Flags for debugging IT on Swarm itself:
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
- `-dry-run`: skip the actual workload
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm

View File

@@ -1,2 +0,0 @@
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773

201
vendor/github.com/projectatomic/libpod/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

96
vendor/github.com/projectatomic/libpod/README.md generated vendored Normal file
View File

@@ -0,0 +1,96 @@
![PODMAN logo](https://cdn.rawgit.com/kubernetes-incubator/cri-o/master/logo/crio-logo.svg)
# libpod - library for running OCI-based containers in Pods
### Status: Development
## What is the scope of this project?
libpod provides a library for applications looking to use the Container Pod concept popularized by Kubernetes.
libpod also contains a tool podman, which allows you to manage Pods, Containers, and Container Images.
At a high level, we expect the scope of libpod/podman to the following functionalities:
* Support multiple image formats including the existing Docker/OCI image formats
* Support for multiple means to download images including trust & image verification
* Container image management (managing image layers, overlay filesystems, etc)
* Container and POD process lifecycle management
* Resource isolation of containers and PODS.
## What is not in scope for this project?
* Building container images. See Buildah
* Signing and pushing images to various image storages. See Skopeo.
* Container Runtimes daemons for working with Kubernetes CRIs See CRI-O.
The plan is to use OCI projects and best of breed libraries for different aspects:
- Runtime: [runc](https://github.com/opencontainers/runc) (or any OCI runtime-spec implementation) and [oci runtime tools](https://github.com/opencontainers/runtime-tools)
- Images: Image management using [containers/image](https://github.com/containers/image)
- Storage: Storage and management of image layers using [containers/storage](https://github.com/containers/storage)
- Networking: Networking support through use of [CNI](https://github.com/containernetworking/cni)
libpod is currently in active development.
## Commands
| Command | Description | Demo|
| :------------------------------------------------------- | :------------------------------------------------------------------------ | :----|
| [podman(1)](/docs/podman.1.md) | Simple management tool for pods and images ||
| [podman-attach(1)](/docs/podman-attach.1.md) | Attach to a running container ||
| [podman-build(1)](/docs/podman-build.1.md) | Build an image using instructions from Dockerfiles ||
| [podman-commit(1)](/docs/podman-commit.1.md) | Create new image based on the changed container ||
| [podman-cp(1)](/docs/podman-cp.1.md) | Instead of providing a `podman cp` command, the man page `podman-cp` describes how to use the `podman mount` command to have even more flexibility and functionality||
| [podman-create(1)](/docs/podman-create.1.md) | Create a new container ||
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[![...](/docs/play.png)](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
| [podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |[![...](/docs/play.png)](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
| [podman-history(1)](/docs/podman-history.1.md) | Shows the history of an image |[![...](/docs/play.png)](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)|
| [podman-images(1)](/docs/podman-images.1.md) | List images in local storage |[![...](/docs/play.png)](https://asciinema.org/a/133649)|
| [podman-import(1)](/docs/podman-import.1.md) | Import a tarball and save it as a filesystem image ||
| [podman-info(1)](/docs/podman-info.1.md) | Display system information ||
| [podman-inspect(1)](/docs/podman-inspect.1.md) | Display the configuration of a container or image |[![...](/docs/play.png)](https://asciinema.org/a/133418)|
| [podman-kill(1)](/docs/podman-kill.1.md) | Kill the main process in one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/3jNos0A5yzO4hChu7ddKkUPw7)|
| [podman-load(1)](/docs/podman-load.1.md) | Load an image from docker archive or oci |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
| [podman-login(1)](/docs/podman-login.1.md) | Login to a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [podman-logout(1)](/docs/podman-logout.1.md) | Logout of a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [podman-logs(1)](/docs/podman-logs.1.md) | Display the logs of a container ||
| [podman-mount(1)](/docs/podman-mount.1.md) | Mount a working container's root filesystem ||
| [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [podman-ps(1)](/docs/podman-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)|
| [podman-pull(1)](/docs/podman-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)|
| [podman-push(1)](/docs/podman-push.1.md) | Push an image to a specified destination |[![...](/docs/play.png)](https://asciinema.org/a/133276)|
| [podman-rm(1)](/docs/podman-rm.1.md) | Removes one or more containers |[![...](/docs/play.png)](https://asciinema.org/a/7EMk22WrfGtKWmgHJX9Nze1Qp)|
| [podman-rmi(1)](/docs/podman-rmi.1.md) | Removes one or more images |[![...](/docs/play.png)](https://asciinema.org/a/133799)|
| [podman-save(1)](/docs/podman-save.1.md) | Saves an image to an archive |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
| [podman-start(1)](/docs/podman-start.1.md) | Starts one or more containers
| [podman-stats(1)](/docs/podman-stats.1.md) | Display a live stream of one or more containers' resource usage statistics||
| [podman-stop(1)](/docs/podman-stop.1.md) | Stops one or more running containers ||
| [podman-tag(1)](/docs/podman-tag.1.md) | Add an additional name to a local image |[![...](/docs/play.png)](https://asciinema.org/a/133803)|
| [podman-top(1)](/docs/podman-top.1.md) | Display the running processes of a container
| [podman-umount(1)](/docs/podman-umount.1.md) | Unmount a working container's root filesystem ||
| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [podman-version(1)](/docs/podman-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
| [podman-wait(1)](/docs/podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes||
## OCI Hooks Support
[PODMAN configures OCI Hooks to run when launching a container](./hooks.md)
## PODMAN Usage Transfer
[Useful information for ops and dev transfer as it relates to infrastructure that utilizes PODMAN](/transfer.md)
## Communication
For async communication and long running discussions please use issues and pull requests on the github repo. This will be the best place to discuss design and implementation.
For sync communication we have an IRC channel #PODMAN, on chat.freenode.net, that everyone is welcome to join and chat about development.
## [Installation Instructions](install.md)
### Current Roadmap
1. Basic pod/container lifecycle, basic image pull (done)
1. Support for tty handling and state management (done)
1. Basic integration with kubelet once client side changes are ready (done)
1. Support for log management, networking integration using CNI, pluggable image/storage management (done)
1. Support for exec/attach (done)

149
vendor/github.com/projectatomic/libpod/conmon/cmsg.c generated vendored Normal file
View File

@@ -0,0 +1,149 @@
/*
* Copyright 2016 SUSE LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTE: This code comes directly from runc/libcontainer/utils/cmsg.c. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "cmsg.h"
#define error(fmt, ...) \
({ \
fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \
errno = ECOMM; \
goto err; /* return value */ \
})
/*
* Sends a file descriptor along the sockfd provided. Returns the return
* value of sendmsg(2). Any synchronisation and preparation of state
* should be done external to this (we expect the other side to be in
* recvfd() in the code).
*/
ssize_t sendfd(int sockfd, struct file_t file)
{
struct msghdr msg = {0};
struct iovec iov[1] = {0};
struct cmsghdr *cmsg;
int *fdptr;
union {
char buf[CMSG_SPACE(sizeof(file.fd))];
struct cmsghdr align;
} u;
/*
* We need to send some other data along with the ancillary data,
* otherwise the other side won't recieve any data. This is very
* well-hidden in the documentation (and only applies to
* SOCK_STREAM). See the bottom part of unix(7).
*/
iov[0].iov_base = file.name;
iov[0].iov_len = strlen(file.name) + 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
fdptr = (int *) CMSG_DATA(cmsg);
memcpy(fdptr, &file.fd, sizeof(int));
return sendmsg(sockfd, &msg, 0);
}
/*
* Receives a file descriptor from the sockfd provided. Returns the file
* descriptor as sent from sendfd(). It will return the file descriptor
* or die (literally) trying. Any synchronisation and preparation of
* state should be done external to this (we expect the other side to be
* in sendfd() in the code).
*/
struct file_t recvfd(int sockfd)
{
struct msghdr msg = {0};
struct iovec iov[1] = {0};
struct cmsghdr *cmsg;
struct file_t file = {0};
int *fdptr;
int olderrno;
union {
char buf[CMSG_SPACE(sizeof(file.fd))];
struct cmsghdr align;
} u;
/* Allocate a buffer. */
/* TODO: Make this dynamic with MSG_PEEK. */
file.name = malloc(TAG_BUFFER);
if (!file.name)
error("recvfd: failed to allocate file.tag buffer\n");
/*
* We need to "recieve" the non-ancillary data even though we don't
* plan to use it at all. Otherwise, things won't work as expected.
* See unix(7) and other well-hidden documentation.
*/
iov[0].iov_base = file.name;
iov[0].iov_len = TAG_BUFFER;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
ssize_t ret = recvmsg(sockfd, &msg, 0);
if (ret < 0)
goto err;
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg)
error("recvfd: got NULL from CMSG_FIRSTHDR");
if (cmsg->cmsg_level != SOL_SOCKET)
error("recvfd: expected SOL_SOCKET in cmsg: %d", cmsg->cmsg_level);
if (cmsg->cmsg_type != SCM_RIGHTS)
error("recvfd: expected SCM_RIGHTS in cmsg: %d", cmsg->cmsg_type);
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
error("recvfd: expected correct CMSG_LEN in cmsg: %lu", cmsg->cmsg_len);
fdptr = (int *) CMSG_DATA(cmsg);
if (!fdptr || *fdptr < 0)
error("recvfd: recieved invalid pointer");
file.fd = *fdptr;
return file;
err:
olderrno = errno;
free(file.name);
errno = olderrno;
return (struct file_t){0};
}

38
vendor/github.com/projectatomic/libpod/conmon/cmsg.h generated vendored Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright 2016 SUSE LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTE: This code comes directly from runc/libcontainer/utils/cmsg.h. */
#pragma once
#if !defined(CMSG_H)
#define CMSG_H
#include <sys/types.h>
/* TODO: Implement this properly with MSG_PEEK. */
#define TAG_BUFFER 4096
/* This mirrors Go's (*os.File). */
struct file_t {
char *name;
int fd;
};
struct file_t recvfd(int sockfd);
ssize_t sendfd(int sockfd, struct file_t file);
#endif /* !defined(CMSG_H) */

1465
vendor/github.com/projectatomic/libpod/conmon/conmon.c generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,18 @@
package buildah
package chrootuser
import (
"os/user"
"strconv"
"strings"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
func getUser(rootdir, userspec string) (specs.User, error) {
// GetUser will return the uid, gid of the user specified in the userspec
// it will use the /etc/password and /etc/shadow files inside of the rootdir
// to return this information.
// userspace format [user | user:group | uid | uid:gid | user:gid | uid:group ]
func GetUser(rootdir, userspec string) (uint32, uint32, error) {
var gid64 uint64
var gerr error = user.UnknownGroupError("error looking up group")
@@ -17,7 +20,7 @@ func getUser(rootdir, userspec string) (specs.User, error) {
userspec = spec[0]
groupspec := ""
if userspec == "" {
return specs.User{}, nil
return 0, 0, nil
}
if len(spec) > 1 {
groupspec = spec[1]
@@ -57,17 +60,12 @@ func getUser(rootdir, userspec string) (specs.User, error) {
}
if uerr == nil && gerr == nil {
u := specs.User{
UID: uint32(uid64),
GID: uint32(gid64),
Username: userspec,
}
return u, nil
return uint32(uid64), uint32(gid64), nil
}
err := errors.Wrapf(uerr, "error determining run uid")
if uerr == nil {
err = errors.Wrapf(gerr, "error determining run gid")
}
return specs.User{}, err
return 0, 0, err
}

View File

@@ -1,6 +1,6 @@
// +build !linux
package buildah
package chrootuser
import (
"github.com/pkg/errors"

View File

@@ -1,6 +1,6 @@
// +build linux
package buildah
package chrootuser
import (
"bufio"
@@ -20,7 +20,7 @@ import (
)
const (
openChrootedCommand = Package + "-open"
openChrootedCommand = "chrootuser-open"
)
func init() {

102
vendor/github.com/projectatomic/libpod/vendor.conf generated vendored Normal file
View File

@@ -0,0 +1,102 @@
#
github.com/sirupsen/logrus v1.0.0
github.com/containers/image 9b4510f6d1627c8e53c3303a8fe48ca7842c2ace
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/ostreedev/ostree-go master
github.com/containers/storage 1824cf917a6b42d8c41179e807bb20a5fd6c0f0a
github.com/containernetworking/cni v0.4.0
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
github.com/opencontainers/go-digest v1.0.0-rc0
github.com/opencontainers/runtime-tools v0.3.0
github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13
github.com/mrunalp/fileutils master
github.com/vishvananda/netlink master
github.com/vishvananda/netns master
github.com/opencontainers/image-spec v1.0.0
github.com/opencontainers/runtime-spec v1.0.0
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
github.com/tchap/go-patricia v2.2.6
gopkg.in/cheggaaa/pb.v1 v1.0.7
gopkg.in/inf.v0 v0.9.0
gopkg.in/yaml.v2 v2
github.com/docker/docker ce452fb72ffcdb7605ce98bde9302238f47c63c5
github.com/docker/spdystream ed496381df8283605c435b86d4fdd6f4f20b8c6e
github.com/docker/distribution 7a8efe719e55bbfaff7bc5718cdf0ed51ca821df
github.com/docker/go-units v0.3.2
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20
github.com/mistifyio/go-zfs v2.1.1
github.com/ghodss/yaml 04f313413ffd65ce25f2541bfd2b2ceec5c0908c
github.com/imdario/mergo 0.2.2
github.com/gorilla/mux v1.3.0
github.com/gorilla/context v1.1
github.com/mtrmac/gpgme b2432428689ca58c2b8e8dea9449d3295cf96fc9
github.com/mattn/go-runewidth v0.0.1
github.com/seccomp/libseccomp-golang v0.9.0
github.com/syndtr/gocapability e7cb7fa329f456b3855136a2642b197bad7366ba
github.com/blang/semver v3.5.0
github.com/BurntSushi/toml v0.2.0
github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
github.com/davecgh/go-spew v1.1.0
github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff
github.com/go-openapi/jsonpointer 779f45308c19820f1a69e9a4cd965f496e0da10f
github.com/go-openapi/jsonreference 36d33bfe519efae5632669801b180bf1a245da3b
github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72
github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
github.com/mailru/easyjson 99e922cf9de1bc0ab38310c277cff32c2147e747
github.com/PuerkitoBio/purell v1.1.0
github.com/PuerkitoBio/urlesc 5bd2802263f21d8788851d5305584c82a5c75d7e
github.com/ugorji/go d23841a297e5489e787e72fceffabf9d2994b52a
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2
golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674
golang.org/x/sys 9aade4d3a3b7e6d876cd3823ad20ec45fc035402
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
github.com/kr/pty v1.0.0
github.com/gogo/protobuf v0.3
github.com/golang/protobuf 748d386b5c1ea99658fd69fe9f03991ce86a90c1
github.com/coreos/go-systemd v14
github.com/coreos/pkg v3
github.com/golang/groupcache b710c8433bd175204919eb38776e944233235d03
github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
github.com/Microsoft/go-winio 78439966b38d69bf38227fbf57ac8a6fee70f69a
github.com/Microsoft/hcsshim 43f9725307998e09f2e3816c2c0c36dc98f0c982
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
github.com/emicklei/go-restful-swagger12 1.0.1
github.com/pkg/errors v0.8.0
github.com/godbus/dbus a389bdde4dd695d414e47b755e95e72b7826432c
github.com/urfave/cli 39908eb08fee7c10d842622a114a5c133fb0a3c6
github.com/vbatts/tar-split v0.10.2
github.com/renstrom/dedent v1.0.0
github.com/hpcloud/tail v1.0.0
gopkg.in/fsnotify.v1 v1.4.2
gopkg.in/tomb.v1 v1
github.com/fatih/camelcase f6a740d52f961c60348ebb109adde9f4635d7540
github.com/buger/goterm 2f8dfbc7dbbff5dd1d391ed91482c24df243b2d3
github.com/dgrijalva/jwt-go v3.0.0
github.com/exponent-io/jsonpath d6023ce2651d8eafb5c75bb0c7167536102ec9f5
github.com/hashicorp/golang-lru 0a025b7e63adc15a622f29b0b2c4c3848243bbf6
github.com/go-openapi/loads 18441dfa706d924a39a030ee2c3b1d8d81917b38
github.com/go-openapi/analysis b44dc874b601d9e4e2f6e19140e794ba24bead3b
github.com/go-openapi/strfmt 93a31ef21ac23f317792fff78f9539219dd74619
github.com/asaskevich/govalidator v6
github.com/go-openapi/errors d24ebc2075bad502fac3a8ae27aa6dd58e1952dc
github.com/mitchellh/mapstructure d0303fe809921458f417bcf828397a65db30a7e4
gopkg.in/mgo.v2 v2
github.com/prometheus/client_golang e7e903064f5e9eb5da98208bae10b475d4db0f8c
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
github.com/prometheus/common 13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207
github.com/prometheus/procfs 65c1f6f8f0fc1e2185eb9863a3bc751496404259
github.com/matttproud/golang_protobuf_extensions fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
github.com/hashicorp/go-multierror 83588e72410abfbe4df460eeb6f30841ae47d4c4
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
github.com/containerd/continuity master
github.com/xeipuuv/gojsonschema master
github.com/xeipuuv/gojsonreference master
github.com/xeipuuv/gojsonpointer master