github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/diff.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/containers/buildah/pkg/formats"
     7  	"github.com/containers/libpod/cmd/podman/cliconfig"
     8  	"github.com/containers/libpod/pkg/adapter"
     9  	"github.com/containers/storage/pkg/archive"
    10  	"github.com/pkg/errors"
    11  	"github.com/spf13/cobra"
    12  )
    13  
    14  type diffJSONOutput struct {
    15  	Changed []string `json:"changed,omitempty"`
    16  	Added   []string `json:"added,omitempty"`
    17  	Deleted []string `json:"deleted,omitempty"`
    18  }
    19  
    20  type diffOutputParams struct {
    21  	Change archive.ChangeType
    22  	Path   string
    23  }
    24  
    25  type stdoutStruct struct {
    26  	output []diffOutputParams
    27  }
    28  
    29  func (so stdoutStruct) Out() error {
    30  	for _, d := range so.output {
    31  		fmt.Printf("%s %s\n", d.Change, d.Path)
    32  	}
    33  	return nil
    34  }
    35  
    36  var (
    37  	diffCommand     cliconfig.DiffValues
    38  	diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem.  The container or image will be compared to its parent layer.`)
    39  
    40  	_diffCommand = &cobra.Command{
    41  		Use:   "diff [flags] CONTAINER | IMAGE",
    42  		Short: "Inspect changes on container's file systems",
    43  		Long:  diffDescription,
    44  		RunE: func(cmd *cobra.Command, args []string) error {
    45  			diffCommand.InputArgs = args
    46  			diffCommand.GlobalFlags = MainGlobalOpts
    47  			diffCommand.Remote = remoteclient
    48  			return diffCmd(&diffCommand)
    49  		},
    50  		Example: `podman diff imageID
    51    podman diff ctrID
    52    podman diff --format json redis:alpine`,
    53  	}
    54  )
    55  
    56  func init() {
    57  	diffCommand.Command = _diffCommand
    58  	diffCommand.SetHelpTemplate(HelpTemplate())
    59  	diffCommand.SetUsageTemplate(UsageTemplate())
    60  	flags := diffCommand.Flags()
    61  
    62  	flags.BoolVar(&diffCommand.Archive, "archive", true, "Save the diff as a tar archive")
    63  	flags.StringVar(&diffCommand.Format, "format", "", "Change the output format")
    64  	flags.BoolVarP(&diffCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
    65  	markFlagHidden(flags, "archive")
    66  	markFlagHiddenForRemoteClient("latest", flags)
    67  
    68  }
    69  
    70  func formatJSON(output []diffOutputParams) (diffJSONOutput, error) {
    71  	jsonStruct := diffJSONOutput{}
    72  	for _, output := range output {
    73  		switch output.Change {
    74  		case archive.ChangeModify:
    75  			jsonStruct.Changed = append(jsonStruct.Changed, output.Path)
    76  		case archive.ChangeAdd:
    77  			jsonStruct.Added = append(jsonStruct.Added, output.Path)
    78  		case archive.ChangeDelete:
    79  			jsonStruct.Deleted = append(jsonStruct.Deleted, output.Path)
    80  		default:
    81  			return jsonStruct, errors.Errorf("output kind %q not recognized", output.Change.String())
    82  		}
    83  	}
    84  	return jsonStruct, nil
    85  }
    86  
    87  func diffCmd(c *cliconfig.DiffValues) error {
    88  	if len(c.InputArgs) != 1 && !c.Latest {
    89  		return errors.Errorf("container, image, or layer name must be specified: podman diff [options [...]] ID-NAME")
    90  	}
    91  
    92  	runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
    93  	if err != nil {
    94  		return errors.Wrapf(err, "could not get runtime")
    95  	}
    96  	defer runtime.DeferredShutdown(false)
    97  
    98  	var to string
    99  	if c.Latest {
   100  		ctr, err := runtime.GetLatestContainer()
   101  		if err != nil {
   102  			return errors.Wrapf(err, "unable to get latest container")
   103  		}
   104  		to = ctr.ID()
   105  	} else {
   106  		to = c.InputArgs[0]
   107  	}
   108  	changes, err := runtime.Diff(c, to)
   109  	if err != nil {
   110  		return errors.Wrapf(err, "could not get changes for %q", to)
   111  	}
   112  	diffOutput := []diffOutputParams{}
   113  	outputFormat := c.Format
   114  
   115  	for _, change := range changes {
   116  
   117  		params := diffOutputParams{
   118  			Change: change.Kind,
   119  			Path:   change.Path,
   120  		}
   121  		diffOutput = append(diffOutput, params)
   122  	}
   123  
   124  	var out formats.Writer
   125  
   126  	if outputFormat != "" {
   127  		switch outputFormat {
   128  		case formats.JSONString:
   129  			data, err := formatJSON(diffOutput)
   130  			if err != nil {
   131  				return err
   132  			}
   133  			out = formats.JSONStruct{Output: data}
   134  		default:
   135  			return errors.New("only valid format for diff is 'json'")
   136  		}
   137  	} else {
   138  		out = stdoutStruct{output: diffOutput}
   139  	}
   140  	return out.Out()
   141  }