mirror of
https://github.com/containers/podman.git
synced 2025-07-02 00:30:00 +08:00
podman diff accept two images or containers
First, make podman diff accept optionally a second argument. This allows the user to specify a second image/container to compare the first with. If it is not set the parent layer will be used as before. Second, podman container diff should only use containers and podman image diff should only use images. Previously, podman container diff would use the image when both an image and container with this name exists. To make this work two new parameters have been added to the api. If they are not used the previous behaviour is used. The same applies to the bindings. Fixes #10649 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
@ -1,10 +1,11 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"github.com/containers/common/pkg/report"
|
||||
"github.com/containers/podman/v3/cmd/podman/common"
|
||||
"github.com/containers/podman/v3/cmd/podman/diff"
|
||||
"github.com/containers/podman/v3/cmd/podman/registry"
|
||||
"github.com/containers/podman/v3/cmd/podman/validate"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@ -13,11 +14,11 @@ import (
|
||||
var (
|
||||
// podman container _diff_
|
||||
diffCmd = &cobra.Command{
|
||||
Use: "diff [options] CONTAINER",
|
||||
Args: validate.IDOrLatestArgs,
|
||||
Use: "diff [options] CONTAINER [CONTAINER]",
|
||||
Args: diff.ValidateContainerDiffArgs,
|
||||
Short: "Inspect changes to the container's file systems",
|
||||
Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`,
|
||||
RunE: diff,
|
||||
Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer or the second argument when given.`,
|
||||
RunE: diffRun,
|
||||
ValidArgsFunction: common.AutocompleteContainers,
|
||||
Example: `podman container diff myCtr
|
||||
podman container diff -l --format json myCtr`,
|
||||
@ -33,41 +34,22 @@ func init() {
|
||||
|
||||
diffOpts = &entities.DiffOptions{}
|
||||
flags := diffCmd.Flags()
|
||||
|
||||
// FIXME: Why does this exists? It is not used anywhere.
|
||||
flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
|
||||
_ = flags.MarkHidden("archive")
|
||||
|
||||
formatFlagName := "format"
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format")
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
|
||||
_ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
|
||||
|
||||
validate.AddLatestFlag(diffCmd, &diffOpts.Latest)
|
||||
}
|
||||
|
||||
func diff(cmd *cobra.Command, args []string) error {
|
||||
func diffRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 && !diffOpts.Latest {
|
||||
return errors.New("container must be specified: podman container diff [options [...]] ID-NAME")
|
||||
}
|
||||
|
||||
var id string
|
||||
if len(args) > 0 {
|
||||
id = args[0]
|
||||
}
|
||||
results, err := registry.ContainerEngine().ContainerDiff(registry.GetContext(), id, *diffOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case report.IsJSON(diffOpts.Format):
|
||||
return common.ChangesToJSON(results)
|
||||
case diffOpts.Format == "":
|
||||
return common.ChangesToTable(results)
|
||||
default:
|
||||
return errors.New("only supported value for '--format' is 'json'")
|
||||
}
|
||||
}
|
||||
|
||||
func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error {
|
||||
diffOpts = &options
|
||||
return diff(cmd, args)
|
||||
diffOpts.Type = define.DiffContainer
|
||||
return diff.Diff(cmd, args, *diffOpts)
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v3/cmd/podman/common"
|
||||
"github.com/containers/podman/v3/cmd/podman/containers"
|
||||
"github.com/containers/podman/v3/cmd/podman/images"
|
||||
"github.com/containers/podman/v3/cmd/podman/diff"
|
||||
"github.com/containers/podman/v3/cmd/podman/registry"
|
||||
"github.com/containers/podman/v3/cmd/podman/validate"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -16,13 +14,13 @@ import (
|
||||
|
||||
var (
|
||||
// Command: podman _diff_ Object_ID
|
||||
diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.`
|
||||
diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer or the second argument when given.`
|
||||
diffCmd = &cobra.Command{
|
||||
Use: "diff [options] {CONTAINER|IMAGE}",
|
||||
Args: validate.IDOrLatestArgs,
|
||||
Use: "diff [options] {CONTAINER|IMAGE} [{CONTAINER|IMAGE}]",
|
||||
Args: diff.ValidateContainerDiffArgs,
|
||||
Short: "Display the changes to the object's file system",
|
||||
Long: diffDescription,
|
||||
RunE: diff,
|
||||
RunE: diffRun,
|
||||
ValidArgsFunction: common.AutocompleteContainersAndImages,
|
||||
Example: `podman diff imageID
|
||||
podman diff ctrID
|
||||
@ -37,36 +35,18 @@ func init() {
|
||||
Command: diffCmd,
|
||||
})
|
||||
flags := diffCmd.Flags()
|
||||
// FIXME: Why does this exists? It is not used anywhere.
|
||||
flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
|
||||
_ = flags.MarkHidden("archive")
|
||||
|
||||
formatFlagName := "format"
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format")
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
|
||||
_ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
|
||||
|
||||
validate.AddLatestFlag(diffCmd, &diffOpts.Latest)
|
||||
}
|
||||
|
||||
func diff(cmd *cobra.Command, args []string) error {
|
||||
// Latest implies looking for a container
|
||||
if diffOpts.Latest {
|
||||
return containers.Diff(cmd, args, diffOpts)
|
||||
}
|
||||
|
||||
options := entities.ContainerExistsOptions{
|
||||
External: true,
|
||||
}
|
||||
if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0], options); err != nil {
|
||||
return err
|
||||
} else if found.Value {
|
||||
return containers.Diff(cmd, args, diffOpts)
|
||||
}
|
||||
|
||||
if found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]); err != nil {
|
||||
return err
|
||||
} else if found.Value {
|
||||
return images.Diff(cmd, args, diffOpts)
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s not found on system", args[0])
|
||||
func diffRun(cmd *cobra.Command, args []string) error {
|
||||
diffOpts.Type = define.DiffAll
|
||||
return diff.Diff(cmd, args, diffOpts)
|
||||
}
|
||||
|
79
cmd/podman/diff/diff.go
Normal file
79
cmd/podman/diff/diff.go
Normal file
@ -0,0 +1,79 @@
|
||||
package diff
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/report"
|
||||
"github.com/containers/podman/v3/cmd/podman/registry"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error {
|
||||
results, err := registry.ContainerEngine().Diff(registry.GetContext(), args, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case report.IsJSON(options.Format):
|
||||
return changesToJSON(results)
|
||||
case options.Format == "":
|
||||
return changesToTable(results)
|
||||
default:
|
||||
return errors.New("only supported value for '--format' is 'json'")
|
||||
}
|
||||
}
|
||||
|
||||
type ChangesReportJSON struct {
|
||||
Changed []string `json:"changed,omitempty"`
|
||||
Added []string `json:"added,omitempty"`
|
||||
Deleted []string `json:"deleted,omitempty"`
|
||||
}
|
||||
|
||||
func changesToJSON(diffs *entities.DiffReport) error {
|
||||
body := ChangesReportJSON{}
|
||||
for _, row := range diffs.Changes {
|
||||
switch row.Kind {
|
||||
case archive.ChangeAdd:
|
||||
body.Added = append(body.Added, row.Path)
|
||||
case archive.ChangeDelete:
|
||||
body.Deleted = append(body.Deleted, row.Path)
|
||||
case archive.ChangeModify:
|
||||
body.Changed = append(body.Changed, row.Path)
|
||||
default:
|
||||
return errors.Errorf("output kind %q not recognized", row.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
// Pull in configured json library
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(body)
|
||||
}
|
||||
|
||||
func changesToTable(diffs *entities.DiffReport) error {
|
||||
for _, row := range diffs.Changes {
|
||||
fmt.Fprintln(os.Stdout, row.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag
|
||||
func ValidateContainerDiffArgs(cmd *cobra.Command, args []string) error {
|
||||
given, _ := cmd.Flags().GetBool("latest")
|
||||
if len(args) > 0 && !given {
|
||||
return cobra.RangeArgs(1, 2)(cmd, args)
|
||||
}
|
||||
if len(args) > 0 && given {
|
||||
return errors.New("--latest and containers cannot be used together")
|
||||
}
|
||||
if len(args) == 0 && !given {
|
||||
return errors.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath())
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"github.com/containers/common/pkg/report"
|
||||
"github.com/containers/podman/v3/cmd/podman/common"
|
||||
"github.com/containers/podman/v3/cmd/podman/diff"
|
||||
"github.com/containers/podman/v3/cmd/podman/registry"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@ -13,11 +13,11 @@ import (
|
||||
var (
|
||||
// podman container _inspect_
|
||||
diffCmd = &cobra.Command{
|
||||
Use: "diff [options] IMAGE",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Use: "diff [options] IMAGE [IMAGE]",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Short: "Inspect changes to the image's file systems",
|
||||
Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`,
|
||||
RunE: diff,
|
||||
Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer or the second argument when given.`,
|
||||
RunE: diffRun,
|
||||
ValidArgsFunction: common.AutocompleteImages,
|
||||
Example: `podman image diff myImage
|
||||
podman image diff --format json redis:alpine`,
|
||||
@ -39,31 +39,11 @@ func diffFlags(flags *pflag.FlagSet) {
|
||||
_ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.")
|
||||
|
||||
formatFlagName := "format"
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format")
|
||||
flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)")
|
||||
_ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil))
|
||||
}
|
||||
|
||||
func diff(cmd *cobra.Command, args []string) error {
|
||||
if diffOpts.Latest {
|
||||
return errors.New("image diff does not support --latest")
|
||||
}
|
||||
|
||||
results, err := registry.ImageEngine().Diff(registry.GetContext(), args[0], *diffOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case report.IsJSON(diffOpts.Format):
|
||||
return common.ChangesToJSON(results)
|
||||
case diffOpts.Format == "":
|
||||
return common.ChangesToTable(results)
|
||||
default:
|
||||
return errors.New("only supported value for '--format' is 'json'")
|
||||
}
|
||||
}
|
||||
|
||||
func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error {
|
||||
diffOpts = &options
|
||||
return diff(cmd, args)
|
||||
func diffRun(cmd *cobra.Command, args []string) error {
|
||||
diffOpts.Type = define.DiffImage
|
||||
return diff.Diff(cmd, args, *diffOpts)
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
.so man1/podman-diff.1
|
54
docs/source/markdown/podman-container-diff.1.md
Normal file
54
docs/source/markdown/podman-container-diff.1.md
Normal file
@ -0,0 +1,54 @@
|
||||
% podman-container-diff(1)
|
||||
|
||||
## NAME
|
||||
podman\-container\-diff - Inspect changes on a container's filesystem
|
||||
|
||||
## SYNOPSIS
|
||||
**podman container diff** [*options*] *container* [*container*]
|
||||
|
||||
## DESCRIPTION
|
||||
Displays changes on a container's filesystem. The container will be compared to its parent layer or the second argument when given.
|
||||
|
||||
The output is prefixed with the following symbols:
|
||||
|
||||
| Symbol | Description |
|
||||
|--------|-------------|
|
||||
| A | A file or directory was added. |
|
||||
| D | A file or directory was deleted. |
|
||||
| C | A file or directory was changed. |
|
||||
|
||||
## OPTIONS
|
||||
|
||||
#### **--format**
|
||||
|
||||
Alter the output into a different format. The only valid format for **podman container diff** is `json`.
|
||||
|
||||
#### **--latest**, **-l**
|
||||
|
||||
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
|
||||
to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client)
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
```
|
||||
# podman container diff container1
|
||||
C /usr
|
||||
C /usr/local
|
||||
C /usr/local/bin
|
||||
A /usr/local/bin/docker-entrypoint.sh
|
||||
```
|
||||
|
||||
```
|
||||
$ podman container diff --format json container1 container2
|
||||
{
|
||||
"added": [
|
||||
"/test"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**
|
||||
|
||||
## HISTORY
|
||||
July 2021, Originally compiled by Paul Holzinger <pholzing@redhat.com>
|
@ -19,7 +19,7 @@ The container command allows you to manage containers
|
||||
| commit | [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. |
|
||||
| cp | [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. |
|
||||
| create | [podman-create(1)](podman-create.1.md) | Create a new container. |
|
||||
| diff | [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. |
|
||||
| diff | [podman-container-diff(1)](podman-container-diff.1.md) | Inspect changes on a container's filesystem |
|
||||
| exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. |
|
||||
| exists | [podman-container-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage |
|
||||
| export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. |
|
||||
|
@ -4,18 +4,24 @@
|
||||
podman\-diff - Inspect changes on a container or image's filesystem
|
||||
|
||||
## SYNOPSIS
|
||||
**podman diff** [*options*] *name*
|
||||
|
||||
**podman container diff** [*options*] *name*
|
||||
**podman diff** [*options*] *container|image* [*container|image*]
|
||||
|
||||
## DESCRIPTION
|
||||
Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer
|
||||
Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer or the second argument when given.
|
||||
|
||||
The output is prefixed with the following symbols:
|
||||
|
||||
| Symbol | Description |
|
||||
|--------|-------------|
|
||||
| A | A file or directory was added. |
|
||||
| D | A file or directory was deleted. |
|
||||
| C | A file or directory was changed. |
|
||||
|
||||
## OPTIONS
|
||||
|
||||
#### **--format**
|
||||
|
||||
Alter the output into a different format. The only valid format for diff is `json`.
|
||||
Alter the output into a different format. The only valid format for **podman diff** is `json`.
|
||||
|
||||
#### **--latest**, **-l**
|
||||
|
||||
@ -25,15 +31,12 @@ to run containers such as CRI-O, the last started container could be from either
|
||||
## EXAMPLE
|
||||
|
||||
```
|
||||
# podman diff redis:alpine
|
||||
C /usr
|
||||
C /usr/local
|
||||
C /usr/local/bin
|
||||
A /usr/local/bin/docker-entrypoint.sh
|
||||
$ podman diff container1
|
||||
A /myscript.sh
|
||||
```
|
||||
|
||||
```
|
||||
# podman diff --format json redis:alpine
|
||||
$ podman diff --format json myimage
|
||||
{
|
||||
"changed": [
|
||||
"/usr",
|
||||
@ -46,8 +49,13 @@ A /usr/local/bin/docker-entrypoint.sh
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
$ podman diff container1 image1
|
||||
A /test
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1)
|
||||
**[podman(1)](podman.1.md)**, **[podman-container-diff(1)](podman-container-diff.1.md)**, **[podman-image-diff(1)](podman-image-diff.1.md)**
|
||||
|
||||
## HISTORY
|
||||
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
|
||||
|
@ -4,21 +4,29 @@
|
||||
podman-image-diff - Inspect changes on an image's filesystem
|
||||
|
||||
## SYNOPSIS
|
||||
**podman image diff** [*options*] *name*
|
||||
**podman image diff** [*options*] *image* [*image*]
|
||||
|
||||
## DESCRIPTION
|
||||
Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer
|
||||
Displays changes on an image's filesystem. The image will be compared to its parent layer or the second argument when given.
|
||||
|
||||
The output is prefixed with the following symbols:
|
||||
|
||||
| Symbol | Description |
|
||||
|--------|-------------|
|
||||
| A | A file or directory was added. |
|
||||
| D | A file or directory was deleted. |
|
||||
| C | A file or directory was changed. |
|
||||
|
||||
## OPTIONS
|
||||
|
||||
#### **--format**
|
||||
|
||||
Alter the output into a different format. The only valid format for diff is `json`.
|
||||
Alter the output into a different format. The only valid format for **podman image diff** is `json`.
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
```
|
||||
# podman diff redis:old redis:alpine
|
||||
$ podman diff redis:old
|
||||
C /usr
|
||||
C /usr/local
|
||||
C /usr/local/bin
|
||||
@ -26,7 +34,7 @@ A /usr/local/bin/docker-entrypoint.sh
|
||||
```
|
||||
|
||||
```
|
||||
# podman diff --format json redis:old redis:alpine
|
||||
$ podman diff --format json redis:old redis:alpine
|
||||
{
|
||||
"changed": [
|
||||
"/usr",
|
||||
|
@ -923,7 +923,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
|
||||
var addToTarFiles []string
|
||||
if !options.IgnoreRootfs {
|
||||
// To correctly track deleted files, let's go through the output of 'podman diff'
|
||||
rootFsChanges, err := c.runtime.GetDiff("", c.ID())
|
||||
rootFsChanges, err := c.runtime.GetDiff("", c.ID(), define.DiffContainer)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error exporting root file-system diff for %q", c.ID())
|
||||
}
|
||||
|
26
libpod/define/diff.go
Normal file
26
libpod/define/diff.go
Normal file
@ -0,0 +1,26 @@
|
||||
package define
|
||||
|
||||
// extra type to use as enum
|
||||
type DiffType uint8
|
||||
|
||||
const (
|
||||
// only diff containers
|
||||
DiffContainer DiffType = 1 << iota
|
||||
// only diff images
|
||||
DiffImage
|
||||
// diff both containers and images
|
||||
DiffAll DiffType = 0b11111111
|
||||
)
|
||||
|
||||
func (d DiffType) String() string {
|
||||
switch d {
|
||||
case DiffAll:
|
||||
return "all"
|
||||
case DiffContainer:
|
||||
return "container"
|
||||
case DiffImage:
|
||||
return "image"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package libpod
|
||||
|
||||
import (
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/libpod/layers"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/pkg/errors"
|
||||
@ -21,14 +22,14 @@ var initInodes = map[string]bool{
|
||||
}
|
||||
|
||||
// GetDiff returns the differences between the two images, layers, or containers
|
||||
func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
|
||||
toLayer, err := r.getLayerID(to)
|
||||
func (r *Runtime) GetDiff(from, to string, diffType define.DiffType) ([]archive.Change, error) {
|
||||
toLayer, err := r.getLayerID(to, diffType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fromLayer := ""
|
||||
if from != "" {
|
||||
fromLayer, err = r.getLayerID(from)
|
||||
fromLayer, err = r.getLayerID(from, diffType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -49,25 +50,30 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
|
||||
// GetLayerID gets a full layer id given a full or partial id
|
||||
// If the id matches a container or image, the id of the top layer is returned
|
||||
// If the id matches a layer, the top layer id is returned
|
||||
func (r *Runtime) getLayerID(id string) (string, error) {
|
||||
var toLayer string
|
||||
toImage, _, err := r.libimageRuntime.LookupImage(id, &libimage.LookupImageOptions{IgnorePlatform: true})
|
||||
if err == nil {
|
||||
return toImage.TopLayer(), nil
|
||||
func (r *Runtime) getLayerID(id string, diffType define.DiffType) (string, error) {
|
||||
var lastErr error
|
||||
if diffType&define.DiffImage == define.DiffImage {
|
||||
toImage, _, err := r.libimageRuntime.LookupImage(id, &libimage.LookupImageOptions{IgnorePlatform: true})
|
||||
if err == nil {
|
||||
return toImage.TopLayer(), nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
targetID, err := r.store.Lookup(id)
|
||||
if err != nil {
|
||||
targetID = id
|
||||
}
|
||||
toCtr, err := r.store.Container(targetID)
|
||||
if err != nil {
|
||||
toLayer, err = layers.FullID(r.store, targetID)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("layer, image, or container %s does not exist", id)
|
||||
if diffType&define.DiffContainer == define.DiffContainer {
|
||||
toCtr, err := r.store.Container(id)
|
||||
if err == nil {
|
||||
return toCtr.LayerID, nil
|
||||
}
|
||||
} else {
|
||||
toLayer = toCtr.LayerID
|
||||
lastErr = err
|
||||
}
|
||||
return toLayer, nil
|
||||
|
||||
if diffType == define.DiffAll {
|
||||
toLayer, err := layers.FullID(r.store, id)
|
||||
if err == nil {
|
||||
return toLayer, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
return "", errors.Wrapf(lastErr, "%s not found", id)
|
||||
}
|
||||
|
@ -4,14 +4,39 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/podman/v3/libpod"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/api/handlers/utils"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func Changes(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
|
||||
query := struct {
|
||||
Parent string `schema:"parent"`
|
||||
DiffType string `schema:"diffType"`
|
||||
}{}
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
var diffType define.DiffType
|
||||
switch query.DiffType {
|
||||
case "", "all":
|
||||
diffType = define.DiffAll
|
||||
case "container":
|
||||
diffType = define.DiffContainer
|
||||
case "image":
|
||||
diffType = define.DiffImage
|
||||
default:
|
||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Errorf("invalid diffType value %q", query.DiffType))
|
||||
return
|
||||
}
|
||||
|
||||
id := utils.GetName(r)
|
||||
changes, err := runtime.GetDiff("", id)
|
||||
changes, err := runtime.GetDiff(query.Parent, id, diffType)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
|
@ -1505,6 +1505,15 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name or id of the container
|
||||
// - in: query
|
||||
// name: parent
|
||||
// type: string
|
||||
// description: specify a second layer which is used to compare against it instead of the parent layer
|
||||
// - in: query
|
||||
// name: diffType
|
||||
// type: string
|
||||
// enum: [all, container, image]
|
||||
// description: select what you want to match, default is all
|
||||
// responses:
|
||||
// 200:
|
||||
// description: Array of Changes
|
||||
|
@ -1286,7 +1286,16 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name or id of the container
|
||||
// description: the name or id of the image
|
||||
// - in: query
|
||||
// name: parent
|
||||
// type: string
|
||||
// description: specify a second layer which is used to compare against it instead of the parent layer
|
||||
// - in: query
|
||||
// name: diffType
|
||||
// type: string
|
||||
// enum: [all, container, image]
|
||||
// description: select what you want to match, default is all
|
||||
// responses:
|
||||
// 200:
|
||||
// description: Array of Changes
|
||||
|
@ -13,13 +13,16 @@ func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive
|
||||
if options == nil {
|
||||
options = new(DiffOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nil, nameOrID)
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -70,7 +70,12 @@ type CreateOptions struct{}
|
||||
|
||||
//go:generate go run ../generator/generator.go DiffOptions
|
||||
// DiffOptions are optional options for creating containers
|
||||
type DiffOptions struct{}
|
||||
type DiffOptions struct {
|
||||
// By the default diff will compare against the parent layer. Change the Parent if you want to compare against something else.
|
||||
Parent *string
|
||||
// Change the type the backend should match. This can be set to "all", "container" or "image".
|
||||
DiffType *string
|
||||
}
|
||||
|
||||
//go:generate go run ../generator/generator.go ExecInspectOptions
|
||||
// ExecInspectOptions are optional options for inspecting
|
||||
|
@ -19,3 +19,35 @@ func (o *DiffOptions) Changed(fieldName string) bool {
|
||||
func (o *DiffOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithParent
|
||||
func (o *DiffOptions) WithParent(value string) *DiffOptions {
|
||||
v := &value
|
||||
o.Parent = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetParent
|
||||
func (o *DiffOptions) GetParent() string {
|
||||
var parent string
|
||||
if o.Parent == nil {
|
||||
return parent
|
||||
}
|
||||
return *o.Parent
|
||||
}
|
||||
|
||||
// WithDiffType
|
||||
func (o *DiffOptions) WithDiffType(value string) *DiffOptions {
|
||||
v := &value
|
||||
o.DiffType = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetDiffType
|
||||
func (o *DiffOptions) GetDiffType() string {
|
||||
var diffType string
|
||||
if o.DiffType == nil {
|
||||
return diffType
|
||||
}
|
||||
return *o.DiffType
|
||||
}
|
||||
|
@ -16,6 +16,10 @@ type RemoveOptions struct {
|
||||
//go:generate go run ../generator/generator.go DiffOptions
|
||||
// DiffOptions are optional options image diffs
|
||||
type DiffOptions struct {
|
||||
// By the default diff will compare against the parent layer. Change the Parent if you want to compare against something else.
|
||||
Parent *string
|
||||
// Change the type the backend should match. This can be set to "all", "container" or "image".
|
||||
DiffType *string
|
||||
}
|
||||
|
||||
//go:generate go run ../generator/generator.go ListOptions
|
||||
|
@ -19,3 +19,35 @@ func (o *DiffOptions) Changed(fieldName string) bool {
|
||||
func (o *DiffOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithParent
|
||||
func (o *DiffOptions) WithParent(value string) *DiffOptions {
|
||||
v := &value
|
||||
o.Parent = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetParent
|
||||
func (o *DiffOptions) GetParent() string {
|
||||
var parent string
|
||||
if o.Parent == nil {
|
||||
return parent
|
||||
}
|
||||
return *o.Parent
|
||||
}
|
||||
|
||||
// WithDiffType
|
||||
func (o *DiffOptions) WithDiffType(value string) *DiffOptions {
|
||||
v := &value
|
||||
o.DiffType = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetDiffType
|
||||
func (o *DiffOptions) GetDiffType() string {
|
||||
var diffType string
|
||||
if o.DiffType == nil {
|
||||
return diffType
|
||||
}
|
||||
return *o.DiffType
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ type ContainerEngine interface {
|
||||
ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error)
|
||||
ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error)
|
||||
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
||||
ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
|
||||
ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
|
||||
ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error)
|
||||
ContainerExists(ctx context.Context, nameOrID string, options ContainerExistsOptions) (*BoolReport, error)
|
||||
@ -52,6 +51,7 @@ type ContainerEngine interface {
|
||||
ContainerUnmount(ctx context.Context, nameOrIDs []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error)
|
||||
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||
Diff(ctx context.Context, namesOrIds []string, options DiffOptions) (*DiffReport, error)
|
||||
Events(ctx context.Context, opts EventsOptions) error
|
||||
GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error)
|
||||
GenerateKube(ctx context.Context, nameOrIDs []string, opts GenerateKubeOptions) (*GenerateKubeReport, error)
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
type ImageEngine interface {
|
||||
Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error)
|
||||
Config(ctx context.Context) (*config.Config, error)
|
||||
Diff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
|
||||
Exists(ctx context.Context, nameOrID string) (*BoolReport, error)
|
||||
History(ctx context.Context, nameOrID string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
|
||||
Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"net"
|
||||
|
||||
buildahDefine "github.com/containers/buildah/define"
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/libpod/events"
|
||||
"github.com/containers/podman/v3/pkg/specgen"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
@ -62,9 +63,10 @@ type InspectOptions struct {
|
||||
|
||||
// All API and CLI diff commands and diff sub-commands use the same options
|
||||
type DiffOptions struct {
|
||||
Format string `json:",omitempty"` // CLI only
|
||||
Latest bool `json:",omitempty"` // API and CLI, only supported by containers
|
||||
Archive bool `json:",omitempty"` // CLI only
|
||||
Format string `json:",omitempty"` // CLI only
|
||||
Latest bool `json:",omitempty"` // API and CLI, only supported by containers
|
||||
Archive bool `json:",omitempty"` // CLI only
|
||||
Type define.DiffType // Type which should be compared
|
||||
}
|
||||
|
||||
// DiffReport provides changes for object
|
||||
|
@ -858,16 +858,26 @@ func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entitie
|
||||
return ps.GetExternalContainerLists(ic.Libpod)
|
||||
}
|
||||
|
||||
// ContainerDiff provides changes to given container
|
||||
func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, opts entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
// Diff provides changes to given container
|
||||
func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
var (
|
||||
base string
|
||||
parent string
|
||||
)
|
||||
if opts.Latest {
|
||||
ctnr, err := ic.Libpod.GetLatestContainer()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get latest container")
|
||||
}
|
||||
nameOrID = ctnr.ID()
|
||||
base = ctnr.ID()
|
||||
}
|
||||
changes, err := ic.Libpod.GetDiff("", nameOrID)
|
||||
if len(namesOrIDs) > 0 {
|
||||
base = namesOrIDs[0]
|
||||
if len(namesOrIDs) > 1 {
|
||||
parent = namesOrIDs[1]
|
||||
}
|
||||
}
|
||||
changes, err := ic.Libpod.GetDiff(parent, base, opts.Type)
|
||||
return &entities.DiffReport{Changes: changes}, err
|
||||
}
|
||||
|
||||
|
@ -403,14 +403,6 @@ func (ir *ImageEngine) Import(ctx context.Context, options entities.ImageImportO
|
||||
return &entities.ImageImportReport{Id: imageID}, nil
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
changes, err := ir.Libpod.GetDiff("", nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &entities.DiffReport{Changes: changes}, nil
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
|
||||
filter, err := libimage.ParseSearchFilter(opts.Filters)
|
||||
if err != nil {
|
||||
|
@ -765,8 +765,18 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
return &report, err
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
changes, err := containers.Diff(ic.ClientCtx, nameOrID, nil)
|
||||
func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
var base string
|
||||
options := new(containers.DiffOptions).WithDiffType(opts.Type.String())
|
||||
if len(namesOrIDs) > 0 {
|
||||
base = namesOrIDs[0]
|
||||
if len(namesOrIDs) > 1 {
|
||||
options.WithParent(namesOrIDs[1])
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("no arguments for diff")
|
||||
}
|
||||
changes, err := containers.Diff(ic.ClientCtx, base, options)
|
||||
return &entities.DiffReport{Changes: changes}, err
|
||||
}
|
||||
|
||||
|
@ -299,16 +299,6 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string,
|
||||
return utils2.UntarToFileSystem(opts.Output, f, nil)
|
||||
}
|
||||
|
||||
// Diff reports the changes to the given image
|
||||
func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
options := new(images.DiffOptions)
|
||||
changes, err := images.Diff(ir.ClientCtx, nameOrID, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &entities.DiffReport{Changes: changes}, nil
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
|
||||
mappedFilters := make(map[string][]string)
|
||||
filters, err := libimage.ParseSearchFilter(opts.Filters)
|
||||
|
@ -1,10 +1,12 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
. "github.com/containers/podman/v3/test/utils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@ -40,13 +42,6 @@ var _ = Describe("Podman diff", func() {
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
|
||||
})
|
||||
|
||||
It("podman container diff of image", func() {
|
||||
session := podmanTest.Podman([]string{"container", "diff", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
|
||||
})
|
||||
|
||||
It("podman diff bogus image", func() {
|
||||
session := podmanTest.Podman([]string{"diff", "1234"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
@ -84,7 +79,11 @@ var _ = Describe("Podman diff", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--name", "diff-test", ALPINE, "touch", "/tmp/diff-test"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"diff", "diff-test"})
|
||||
if !IsRemote() {
|
||||
session = podmanTest.Podman([]string{"diff", "-l"})
|
||||
} else {
|
||||
session = podmanTest.Podman([]string{"diff", "diff-test"})
|
||||
}
|
||||
session.WaitWithDefaultTimeout()
|
||||
containerDiff := session.OutputToStringArray()
|
||||
sort.Strings(containerDiff)
|
||||
@ -92,4 +91,101 @@ var _ = Describe("Podman diff", func() {
|
||||
Expect(session.LineInOutputContains("A /tmp/diff-test")).To(BeTrue())
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman image diff", func() {
|
||||
file1 := "/" + stringid.GenerateNonCryptoID()
|
||||
file2 := "/" + stringid.GenerateNonCryptoID()
|
||||
file3 := "/" + stringid.GenerateNonCryptoID()
|
||||
|
||||
// Create container image with the files
|
||||
containerfile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN touch %s
|
||||
RUN touch %s
|
||||
RUN touch %s`, ALPINE, file1, file2, file3)
|
||||
|
||||
image := "podman-diff-test"
|
||||
podmanTest.BuildImage(containerfile, image, "true")
|
||||
|
||||
// build a second image which used as base to compare against
|
||||
// using ALPINE does not work in CI, most likely due the extra vfs.imagestore
|
||||
containerfile = fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN echo test
|
||||
`, ALPINE)
|
||||
baseImage := "base-image"
|
||||
podmanTest.BuildImage(containerfile, baseImage, "true")
|
||||
|
||||
session := podmanTest.Podman([]string{"image", "diff", image})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 1))
|
||||
Expect(session.OutputToString()).To(Equal("A " + file3))
|
||||
|
||||
session = podmanTest.Podman([]string{"image", "diff", image, baseImage})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 4))
|
||||
Expect(session.LineInOutputContains("A " + file1)).To(BeTrue())
|
||||
Expect(session.LineInOutputContains("A " + file2)).To(BeTrue())
|
||||
Expect(session.LineInOutputContains("A " + file3)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman image diff of single image", func() {
|
||||
session := podmanTest.Podman([]string{"image", "diff", BB})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
|
||||
})
|
||||
|
||||
It("podman image diff bogus image", func() {
|
||||
session := podmanTest.Podman([]string{"image", "diff", "1234", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(125))
|
||||
})
|
||||
|
||||
It("podman image diff of the same image", func() {
|
||||
session := podmanTest.Podman([]string{"image", "diff", ALPINE, ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 0))
|
||||
})
|
||||
|
||||
It("podman diff container and image with same name", func() {
|
||||
imagefile := "/" + stringid.GenerateNonCryptoID()
|
||||
confile := "/" + stringid.GenerateNonCryptoID()
|
||||
|
||||
// Create container image with the files
|
||||
containerfile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN touch %s`, ALPINE, imagefile)
|
||||
|
||||
name := "podman-diff-test"
|
||||
podmanTest.BuildImage(containerfile, name, "false")
|
||||
|
||||
session := podmanTest.Podman([]string{"run", "--name", name, ALPINE, "touch", confile})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
// podman diff prefers image over container when they have the same name
|
||||
session = podmanTest.Podman([]string{"diff", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(imagefile))
|
||||
|
||||
session = podmanTest.Podman([]string{"image", "diff", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(imagefile))
|
||||
|
||||
// container diff has to show the container
|
||||
session = podmanTest.Podman([]string{"container", "diff", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(confile))
|
||||
})
|
||||
|
||||
})
|
||||
|
Reference in New Issue
Block a user