From 8ae28a55acc51a02597b23140916a690fbbdc3fc Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Mon, 6 Apr 2020 16:40:32 -0700 Subject: V2 podman diff(changes) support * Ported CLI command * Added API endpoint * Added bindings * Updated swagger (TODO: n endpoints, one handler) Signed-off-by: Jhon Honce --- cmd/podmanV2/containers/diff.go | 67 +++++++++++++++++++++++++++++++++++ cmd/podmanV2/diff.go | 74 +++++++++++++++++++++++++++++++++++++++ cmd/podmanV2/images/diff.go | 63 +++++++++++++++++++++++++++++++++ cmd/podmanV2/inspect.go | 2 +- cmd/podmanV2/registry/registry.go | 8 +++++ cmd/podmanV2/report/diff.go | 44 +++++++++++++++++++++++ 6 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 cmd/podmanV2/containers/diff.go create mode 100644 cmd/podmanV2/diff.go create mode 100644 cmd/podmanV2/images/diff.go create mode 100644 cmd/podmanV2/report/diff.go (limited to 'cmd/podmanV2') diff --git a/cmd/podmanV2/containers/diff.go b/cmd/podmanV2/containers/diff.go new file mode 100644 index 000000000..3009cdfad --- /dev/null +++ b/cmd/podmanV2/containers/diff.go @@ -0,0 +1,67 @@ +package containers + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/report" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + // podman container _diff_ + diffCmd = &cobra.Command{ + Use: "diff [flags] CONTAINER", + Args: registry.IdOrLatestArgs, + Short: "Inspect changes on container's file systems", + Long: `Displays changes on a container filesystem. The container will be compared to its parent layer.`, + PreRunE: preRunE, + RunE: diff, + Example: `podman container diff myCtr + podman container diff -l --format json myCtr`, + } + diffOpts *entities.DiffOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: diffCmd, + Parent: containerCmd, + }) + + diffOpts = &entities.DiffOptions{} + flags := diffCmd.Flags() + flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") + _ = flags.MarkHidden("archive") + flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") + + if !registry.IsRemote() { + flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + } +} + +func diff(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") + } + + results, err := registry.ContainerEngine().ContainerDiff(registry.GetContext(), args[0], entities.DiffOptions{}) + if err != nil { + return err + } + + switch diffOpts.Format { + case "": + return report.ChangesToTable(results) + case "json": + return report.ChangesToJSON(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) +} diff --git a/cmd/podmanV2/diff.go b/cmd/podmanV2/diff.go new file mode 100644 index 000000000..6e4263370 --- /dev/null +++ b/cmd/podmanV2/diff.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/containers" + "github.com/containers/libpod/cmd/podmanV2/images" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +// Inspect is one of the outlier commands in that it operates on images/containers/... + +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.` + diffCmd = &cobra.Command{ + Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", + Args: registry.IdOrLatestArgs, + Short: "Display the changes of object's file system", + Long: diffDescription, + TraverseChildren: true, + RunE: diff, + Example: `podman diff imageID + podman diff ctrID + podman diff --format json redis:alpine`, + } + + diffOpts = entities.DiffOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: diffCmd, + }) + diffCmd.SetHelpTemplate(registry.HelpTemplate()) + diffCmd.SetUsageTemplate(registry.UsageTemplate()) + + flags := diffCmd.Flags() + flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") + _ = flags.MarkHidden("archive") + flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") + + if !registry.IsRemote() { + flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + } +} + +func diff(cmd *cobra.Command, args []string) error { + ie, err := registry.NewImageEngine(cmd, args) + if err != nil { + return err + } + + if found, err := ie.Exists(registry.GetContext(), args[0]); err != nil { + return err + } else if found.Value { + return images.Diff(cmd, args, diffOpts) + } + + ce, err := registry.NewContainerEngine(cmd, args) + if err != nil { + return err + } + + if found, err := ce.ContainerExists(registry.GetContext(), args[0]); err != nil { + return err + } else if found.Value { + return containers.Diff(cmd, args, diffOpts) + } + return fmt.Errorf("%s not found on system", args[0]) +} diff --git a/cmd/podmanV2/images/diff.go b/cmd/podmanV2/images/diff.go new file mode 100644 index 000000000..e913a603a --- /dev/null +++ b/cmd/podmanV2/images/diff.go @@ -0,0 +1,63 @@ +package images + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/report" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + // podman container _inspect_ + diffCmd = &cobra.Command{ + Use: "diff [flags] CONTAINER", + Args: registry.IdOrLatestArgs, + Short: "Inspect changes on image's file systems", + Long: `Displays changes on a image's filesystem. The image will be compared to its parent layer.`, + PreRunE: preRunE, + RunE: diff, + Example: `podman image diff myImage + podman image diff --format json redis:alpine`, + } + diffOpts *entities.DiffOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: diffCmd, + Parent: imageCmd, + }) + + diffOpts = &entities.DiffOptions{} + flags := diffCmd.Flags() + flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") + _ = flags.MarkHidden("archive") + flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") +} + +func diff(cmd *cobra.Command, args []string) error { + if len(args) == 0 && !diffOpts.Latest { + return errors.New("image must be specified: podman image diff [options [...]] ID-NAME") + } + + results, err := registry.ImageEngine().Diff(registry.GetContext(), args[0], entities.DiffOptions{}) + if err != nil { + return err + } + + switch diffOpts.Format { + case "": + return report.ChangesToTable(results) + case "json": + return report.ChangesToJSON(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) +} diff --git a/cmd/podmanV2/inspect.go b/cmd/podmanV2/inspect.go index 4975cf632..15d7579ea 100644 --- a/cmd/podmanV2/inspect.go +++ b/cmd/podmanV2/inspect.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/cobra" ) -// Inspect is one of the out layer commands in that it operates on images/containers/... +// Inspect is one of the outlier commands in that it operates on images/containers/... var ( inspectOpts *entities.InspectOptions diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index 401f82718..8ff44041f 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -113,6 +113,14 @@ func SubCommandExists(cmd *cobra.Command, args []string) error { return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath()) } +// IdOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag +func IdOrLatestArgs(cmd *cobra.Command, args []string) error { + if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) { + return errors.New(`command requires a name, id or the "--latest" flag`) + } + return nil +} + type podmanContextKey string var podmanFactsKey = podmanContextKey("engineOptions") diff --git a/cmd/podmanV2/report/diff.go b/cmd/podmanV2/report/diff.go new file mode 100644 index 000000000..b36189d75 --- /dev/null +++ b/cmd/podmanV2/report/diff.go @@ -0,0 +1,44 @@ +package report + +import ( + "fmt" + "os" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/storage/pkg/archive" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +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) + } + } + + json := jsoniter.ConfigCompatibleWithStandardLibrary + enc := json.NewEncoder(os.Stdout) + return enc.Encode(body) +} + +func ChangesToTable(diffs *entities.DiffReport) error { + for _, row := range diffs.Changes { + fmt.Fprintln(os.Stdout, row.String()) + } + return nil +} -- cgit v1.2.3-54-g00ecf