Skip to content

Commit

Permalink
podman diff accept two images or containers
Browse files Browse the repository at this point in the history
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 containers#10649

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Luap99 committed Jul 2, 2021
1 parent fd17155 commit 8f6a024
Show file tree
Hide file tree
Showing 28 changed files with 512 additions and 172 deletions.
42 changes: 12 additions & 30 deletions cmd/podman/containers/diff.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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`,
Expand All @@ -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)
}
42 changes: 11 additions & 31 deletions cmd/podman/diff.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand All @@ -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
Expand All @@ -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 changes: 79 additions & 0 deletions cmd/podman/diff/diff.go
Original file line number Diff line number Diff line change
@@ -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
}
40 changes: 10 additions & 30 deletions cmd/podman/images/diff.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
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"
)

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`,
Expand All @@ -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 change: 0 additions & 1 deletion docs/source/markdown/links/podman-container-diff.1

This file was deleted.

54 changes: 54 additions & 0 deletions docs/source/markdown/podman-container-diff.1.md
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
2 changes: 1 addition & 1 deletion docs/source/markdown/podman-container.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
Loading

0 comments on commit 8f6a024

Please sign in to comment.