aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md2
-rw-r--r--cmd/podman/imagefilters/filters.go10
-rw-r--r--cmd/podman/images.go43
-rw-r--r--cmd/podman/rmi.go4
-rw-r--r--cmd/podman/varlink/io.podman.varlink3
-rw-r--r--docs/podman-images.1.md64
-rw-r--r--libpod/image/image.go24
-rw-r--r--libpod/image/prune.go2
-rw-r--r--pkg/adapter/runtime.go13
-rw-r--r--pkg/adapter/runtime_remote.go19
-rw-r--r--pkg/varlinkapi/images.go4
-rw-r--r--test/e2e/images_test.go16
12 files changed, 161 insertions, 43 deletions
diff --git a/API.md b/API.md
index febd094df..ec8512fd4 100755
--- a/API.md
+++ b/API.md
@@ -1592,6 +1592,8 @@ labels [map[string]](#map[string])
isParent [bool](https://godoc.org/builtin#bool)
topLayer [string](https://godoc.org/builtin#string)
+
+readOnly [bool](https://godoc.org/builtin#bool)
### <a name="ImageHistory"></a>type ImageHistory
ImageHistory describes the returned structure from ImageHistory.
diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go
index aa5776599..0b08314ce 100644
--- a/cmd/podman/imagefilters/filters.go
+++ b/cmd/podman/imagefilters/filters.go
@@ -46,6 +46,16 @@ func DanglingFilter(danglingImages bool) ResultFilter {
}
}
+// ReadOnlyFilter allows you to filter images based on read/only and read/write
+func ReadOnlyFilter(readOnly bool) ResultFilter {
+ return func(i *adapter.ContainerImage) bool {
+ if readOnly {
+ return i.IsReadOnly()
+ }
+ return !i.IsReadOnly()
+ }
+}
+
// LabelFilter allows you to filter by images labels key and/or value
func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
// We need to handle both label=key and label=key=value
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index f842573d9..281d93e71 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -30,14 +30,16 @@ type imagesTemplateParams struct {
Created string
CreatedTime time.Time
Size string
+ ReadOnly bool
}
type imagesJSONParams struct {
- ID string `json:"id"`
- Name []string `json:"names"`
- Digest digest.Digest `json:"digest"`
- Created time.Time `json:"created"`
- Size *uint64 `json:"size"`
+ ID string `json:"id"`
+ Name []string `json:"names"`
+ Digest digest.Digest `json:"digest"`
+ Created time.Time `json:"created"`
+ Size *uint64 `json:"size"`
+ ReadOnly bool `json:"readonly"`
}
type imagesOptions struct {
@@ -49,6 +51,7 @@ type imagesOptions struct {
outputformat string
sort string
all bool
+ useReadOnly bool
}
// Type declaration and functions for sorting the images output
@@ -175,6 +178,13 @@ func imagesCmd(c *cliconfig.ImagesValues) error {
return errors.Wrapf(err, "unable to get images")
}
+ for _, image := range images {
+ if image.IsReadOnly() {
+ opts.outputformat += "{{.ReadOnly}}\t"
+ break
+ }
+ }
+
var filteredImages []*adapter.ContainerImage
//filter the images
if len(c.Filter) > 0 || len(c.InputArgs) == 1 {
@@ -282,6 +292,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma
CreatedTime: createdTime,
Created: units.HumanDuration(time.Since(createdTime)) + " ago",
Size: sizeStr,
+ ReadOnly: img.IsReadOnly(),
}
imagesOutput = append(imagesOutput, params)
if opts.quiet { // Show only one image ID when quiet
@@ -305,11 +316,12 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage)
size = nil
}
params := imagesJSONParams{
- ID: img.ID(),
- Name: img.Names(),
- Digest: img.Digest(),
- Created: img.Created(),
- Size: size,
+ ID: img.ID(),
+ Name: img.Names(),
+ Digest: img.Digest(),
+ Created: img.Created(),
+ Size: size,
+ ReadOnly: img.IsReadOnly(),
}
imagesOutput = append(imagesOutput, params)
}
@@ -351,6 +363,11 @@ func GenImageOutputMap() map[string]string {
if value == "ID" {
value = "Image" + value
}
+
+ if value == "ReadOnly" {
+ values[key] = "R/O"
+ continue
+ }
values[key] = strings.ToUpper(splitCamelCase(value))
}
return values
@@ -378,6 +395,12 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created()))
+ case "readonly":
+ readonly, err := strconv.ParseBool(splitFilter[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
+ }
+ filterFuncs = append(filterFuncs, imagefilters.ReadOnlyFilter(readonly))
case "dangling":
danglingImages, err := strconv.ParseBool(splitFilter[1])
if err != nil {
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index 9229d497e..57e78c34a 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -87,7 +87,7 @@ func rmiCmd(c *cliconfig.RmiValues) error {
if removeAll {
var imagesToDelete []*adapter.ContainerImage
- imagesToDelete, err = runtime.GetImages()
+ imagesToDelete, err = runtime.GetRWImages()
if err != nil {
return errors.Wrapf(err, "unable to query local images")
}
@@ -107,7 +107,7 @@ func rmiCmd(c *cliconfig.RmiValues) error {
removeImage(i)
}
lastNumberofImages = len(imagesToDelete)
- imagesToDelete, err = runtime.GetImages()
+ imagesToDelete, err = runtime.GetRWImages()
if err != nil {
return err
}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 0bf236b77..72b15c328 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -69,7 +69,8 @@ type Image (
containers: int,
labels: [string]string,
isParent: bool,
- topLayer: string
+ topLayer: string,
+ readOnly: bool
)
# ImageHistory describes the returned structure from ImageHistory.
diff --git a/docs/podman-images.1.md b/docs/podman-images.1.md
index 2a732de57..6360bf580 100644
--- a/docs/podman-images.1.md
+++ b/docs/podman-images.1.md
@@ -23,6 +23,26 @@ Show image digests
Filter output based on conditions provided
+ Filters:
+
+ **after==TIMESTRING**
+ Filter on images created after the given time.Time.
+
+ **before==TIMESTRING**
+ Filter on images created before the given time.Time.
+
+ **dangling=true|false**
+ Show dangling images. Dangling images are a file system layer that was used in a previous build of an image and is no longer referenced by any active images. They are denoted with the <none> tag, consume disk space and serve no active purpose.
+
+ **label**
+ Filter by images labels key and/or value.
+
+ **readonly=true|false**
+ Show only read only images or Read/Write images. The default is to show both. Read/Only images can be configured by modifying the "additionalimagestores" in the /etc/containers/storage.conf file.
+
+ **reference=**
+ Filter by image name, specified as regular expressions.
+
**--format**=*format*
Change the default output format. This can be of a supported type like 'json'
@@ -94,31 +114,31 @@ REPOSITORY TAG IMAGE ID CREATED SIZE
# podman images --format json
[
{
- "id": "e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27",
- "names": [
- "docker.io/kubernetes/pause:latest"
- ],
- "digest": "sha256:0aecf73ff86844324847883f2e916d3f6984c5fae3c2f23e91d66f549fe7d423",
- "created": "2014-07-19T07:02:32.267701596Z",
- "size": 250665
+ "id": "e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27",
+ "names": [
+ "docker.io/kubernetes/pause:latest"
+ ],
+ "digest": "sha256:0aecf73ff86844324847883f2e916d3f6984c5fae3c2f23e91d66f549fe7d423",
+ "created": "2014-07-19T07:02:32.267701596Z",
+ "size": 250665
},
{
- "id": "ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9",
- "names": [
- "\u003cnone\u003e"
- ],
- "digest": "sha256:ba7e4091d27e8114a205003ca6a768905c3395d961624a2c78873d9526461032",
- "created": "2017-10-26T03:07:22.796184288Z",
- "size": 27170520
+ "id": "ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9",
+ "names": [
+ "\u003cnone\u003e"
+ ],
+ "digest": "sha256:ba7e4091d27e8114a205003ca6a768905c3395d961624a2c78873d9526461032",
+ "created": "2017-10-26T03:07:22.796184288Z",
+ "size": 27170520
},
{
- "id": "4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a",
- "names": [
- "docker.io/library/ubuntu:latest"
- ],
- "digest": "sha256:193f7734ddd68e0fb24ba9af8c2b673aecb0227b026871f8e932dab45add7753",
- "created": "2017-10-10T20:59:05.10196344Z",
- "size": 126085200
+ "id": "4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a",
+ "names": [
+ "docker.io/library/ubuntu:latest"
+ ],
+ "digest": "sha256:193f7734ddd68e0fb24ba9af8c2b673aecb0227b026871f8e932dab45add7753",
+ "created": "2017-10-10T20:59:05.10196344Z",
+ "size": 126085200
}
]
```
@@ -148,7 +168,7 @@ docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
```
## SEE ALSO
-podman(1)
+podman(1), containers-storage.conf(5)
## HISTORY
March 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/libpod/image/image.go b/libpod/image/image.go
index f9879b85b..a057bc720 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -54,7 +54,6 @@ type Image struct {
inspect.ImageResult
inspectInfo *types.ImageInspectInfo
InputName string
- Local bool
//runtime *libpod.Runtime
image *storage.Image
imageruntime *Runtime
@@ -119,7 +118,6 @@ func setStore(options storage.StoreOptions) (storage.Store, error) {
func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
image := Image{
InputName: img.ID,
- Local: true,
imageruntime: ir,
image: img,
}
@@ -132,7 +130,6 @@ func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
func (ir *Runtime) NewFromLocal(name string) (*Image, error) {
image := Image{
InputName: name,
- Local: true,
imageruntime: ir,
}
localImage, err := image.getLocalImage()
@@ -153,13 +150,11 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
// We don't know if the image is local or not ... check local first
newImage := Image{
InputName: name,
- Local: false,
imageruntime: ir,
}
if !forcePull {
localImage, err := newImage.getLocalImage()
if err == nil {
- newImage.Local = true
newImage.image = localImage
return &newImage, nil
}
@@ -199,7 +194,6 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im
for _, name := range imageNames {
newImage := Image{
InputName: name,
- Local: true,
imageruntime: ir,
}
img, err := newImage.getLocalImage()
@@ -299,6 +293,11 @@ func (i *Image) ID() string {
return i.image.ID
}
+// IsReadOnly returns whether the image ID comes from a local store
+func (i *Image) IsReadOnly() bool {
+ return i.image.ReadOnly
+}
+
// Digest returns the image's digest
func (i *Image) Digest() digest.Digest {
return i.image.Digest
@@ -439,12 +438,25 @@ func (ir *Runtime) getImage(image string) (*Image, error) {
// GetImages retrieves all images present in storage
func (ir *Runtime) GetImages() ([]*Image, error) {
+ return ir.getImages(false)
+}
+
+// GetRWImages retrieves all read/write images present in storage
+func (ir *Runtime) GetRWImages() ([]*Image, error) {
+ return ir.getImages(true)
+}
+
+// getImages retrieves all images present in storage
+func (ir *Runtime) getImages(rwOnly bool) ([]*Image, error) {
var newImages []*Image
images, err := ir.store.Images()
if err != nil {
return nil, err
}
for _, i := range images {
+ if rwOnly && i.ReadOnly {
+ continue
+ }
// iterating over these, be careful to not iterate on the literal
// pointer.
image := i
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index a4f8a0c9f..6ef5d321f 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -12,7 +12,7 @@ func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
var (
pruneImages []*Image
)
- allImages, err := ir.GetImages()
+ allImages, err := ir.GetRWImages()
if err != nil {
return nil, err
}
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index e65f07898..dc193c738 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -85,16 +85,27 @@ func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) {
// GetImages returns a slice of images in containerimages
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
+ return r.getImages(false)
+}
+
+// GetRWImages returns a slice of read/write images in containerimages
+func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) {
+ return r.getImages(true)
+}
+
+func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
var containerImages []*ContainerImage
images, err := r.Runtime.ImageRuntime().GetImages()
if err != nil {
return nil, err
}
for _, i := range images {
+ if rwOnly && i.IsReadOnly() {
+ continue
+ }
containerImages = append(containerImages, &ContainerImage{i})
}
return containerImages, nil
-
}
// NewImageFromLocal returns a containerimage representation of a image from local storage
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index db3f23629..9fae39df0 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -129,6 +129,7 @@ type remoteImage struct {
isParent bool
Runtime *LocalRuntime
TopLayer string
+ ReadOnly bool
}
// Container ...
@@ -169,12 +170,24 @@ type remoteVolume struct {
// GetImages returns a slice of containerimages over a varlink connection
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
+ return r.getImages(false)
+}
+
+// GetRWImages returns a slice of read/write containerimages over a varlink connection
+func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) {
+ return r.getImages(true)
+}
+
+func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
var newImages []*ContainerImage
images, err := iopodman.ListImages().Call(r.Conn)
if err != nil {
return nil, err
}
for _, i := range images {
+ if rwOnly && i.ReadOnly {
+ continue
+ }
name := i.Id
if len(i.RepoTags) > 1 {
name = i.RepoTags[0]
@@ -207,6 +220,7 @@ func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRu
isParent: i.IsParent,
Runtime: runtime,
TopLayer: i.TopLayer,
+ ReadOnly: i.ReadOnly,
}
return &ContainerImage{ri}, nil
}
@@ -302,6 +316,11 @@ func (ci *ContainerImage) Created() time.Time {
return ci.remoteImage.Created
}
+// IsReadOnly returns whether the image is ReadOnly
+func (ci *ContainerImage) IsReadOnly() bool {
+ return ci.remoteImage.ReadOnly
+}
+
// Size returns the size of the image
func (ci *ContainerImage) Size(ctx context.Context) (*uint64, error) {
usize := uint64(ci.remoteImage.Size)
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 2bebfd406..0ead1c23d 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -69,6 +69,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
Containers: int64(len(containers)),
Labels: labels,
IsParent: isParent,
+ ReadOnly: image.IsReadOnly(),
}
imageList = append(imageList, i)
}
@@ -98,6 +99,8 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
return err
}
+ fmt.Println("DAN isReadOnly %d", newImage.IsReadOnly())
+
il := iopodman.Image{
Id: newImage.ID(),
ParentId: newImage.Parent,
@@ -109,6 +112,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
Containers: int64(len(containers)),
Labels: labels,
TopLayer: newImage.TopLayer(),
+ ReadOnly: newImage.IsReadOnly(),
}
return call.ReplyGetImage(il)
}
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index b6dae33ee..4eadc77e7 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -388,4 +388,20 @@ LABEL "com.example.vendor"="Example Vendor"
output = session.OutputToString()
Expect(output).To(Equal("[]"))
})
+
+ It("podman images --filter readonly", func() {
+ SkipIfRemote()
+ dockerfile := `FROM docker.io/library/alpine:latest
+`
+ podmanTest.BuildImage(dockerfile, "foobar.com/before:latest", "false")
+ result := podmanTest.Podman([]string{"images", "-f", "readonly=true"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+
+ result1 := podmanTest.Podman([]string{"images", "--filter", "readonly=false"})
+ result1.WaitWithDefaultTimeout()
+ Expect(result1.ExitCode()).To(Equal(0))
+ Expect(result.OutputToStringArray()).To(Not(Equal(result1.OutputToStringArray())))
+ })
+
})