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 ++++++++++++++++++++ pkg/api/handlers/compat/changes.go | 20 +++++++++ pkg/api/handlers/compat/swagger.go | 10 +++++ pkg/api/server/register_containers.go | 35 ++++++++++++++++ pkg/api/server/register_images.go | 31 ++++++++++++++ pkg/bindings/containers/diff.go | 24 +++++++++++ pkg/bindings/images/diff.go | 24 +++++++++++ pkg/domain/entities/engine_container.go | 13 +++--- pkg/domain/entities/engine_image.go | 9 ++-- pkg/domain/entities/types.go | 13 ++++++ pkg/domain/infra/abi/containers.go | 17 +++++++- pkg/domain/infra/abi/images.go | 8 ++++ pkg/domain/infra/tunnel/containers.go | 5 +++ pkg/domain/infra/tunnel/images.go | 9 ++++ 19 files changed, 462 insertions(+), 14 deletions(-) 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 create mode 100644 pkg/api/handlers/compat/changes.go create mode 100644 pkg/bindings/containers/diff.go create mode 100644 pkg/bindings/images/diff.go 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 +} diff --git a/pkg/api/handlers/compat/changes.go b/pkg/api/handlers/compat/changes.go new file mode 100644 index 000000000..6907c487e --- /dev/null +++ b/pkg/api/handlers/compat/changes.go @@ -0,0 +1,20 @@ +package compat + +import ( + "net/http" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/api/handlers/utils" +) + +func Changes(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + id := utils.GetName(r) + changes, err := runtime.GetDiff("", id) + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteJSON(w, 200, changes) +} diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go index cbd8e61fb..f1aabf987 100644 --- a/pkg/api/handlers/compat/swagger.go +++ b/pkg/api/handlers/compat/swagger.go @@ -2,6 +2,7 @@ package compat import ( "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/storage/pkg/archive" ) // Create container @@ -25,3 +26,12 @@ type swagCtrWaitResponse struct { } } } + +// Object Changes +// swagger:response Changes +type swagChangesResponse struct { + // in:body + Body struct { + Changes []archive.Change + } +} diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index f126112d0..150cca872 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1377,5 +1377,40 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost) + + // swagger:operation GET /containers/{name}/changes libpod libpodChangesContainer + // swagger:operation GET /libpod/containers/{name}/changes compat changesContainer + // --- + // tags: + // - containers + // - containers (compat) + // summary: Report on changes to container's filesystem; adds, deletes or modifications. + // description: | + // Returns which files in a container's filesystem have been added, deleted, or modified. The Kind of modification can be one of: + // + // 0: Modified + // 1: Added + // 2: Deleted + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or id of the container + // responses: + // 200: + // description: Array of Changes + // content: + // application/json: + // schema: + // $ref: "#/responses/Changes" + // 404: + // $ref: "#/responses/NoSuchContainer" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/containers/{name}/changes"), s.APIHandler(compat.Changes)) + r.HandleFunc("/containers/{name}/changes", s.APIHandler(compat.Changes)) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/changes"), s.APIHandler(compat.Changes)) + return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index d45423096..77560e789 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1125,5 +1125,36 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost) + + // swagger:operation GET /libpod/images/{name}/changes libpod libpodChangesImages + // --- + // tags: + // - images + // summary: Report on changes to images's filesystem; adds, deletes or modifications. + // description: | + // Returns which files in a images's filesystem have been added, deleted, or modified. The Kind of modification can be one of: + // + // 0: Modified + // 1: Added + // 2: Deleted + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or id of the container + // responses: + // 200: + // description: Array of Changes + // content: + // application/json: + // schema: + // $ref: "#/responses/Changes" + // 404: + // $ref: "#/responses/NoSuchContainer" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/images/{name}/changes"), s.APIHandler(compat.Changes)) + return nil } diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go new file mode 100644 index 000000000..82070ca9a --- /dev/null +++ b/pkg/bindings/containers/diff.go @@ -0,0 +1,24 @@ +package containers + +import ( + "context" + "net/http" + + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/storage/pkg/archive" +) + +// Diff provides the changes between two container layers +func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nameOrId) + if err != nil { + return nil, err + } + var changes []archive.Change + return changes, response.Process(&changes) +} diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go new file mode 100644 index 000000000..cfdd06a97 --- /dev/null +++ b/pkg/bindings/images/diff.go @@ -0,0 +1,24 @@ +package images + +import ( + "context" + "net/http" + + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/storage/pkg/archive" +) + +// Diff provides the changes between two container layers +func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nameOrId) + if err != nil { + return nil, err + } + var changes []archive.Change + return changes, response.Process(&changes) +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 576ce1658..e7fa67209 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -9,18 +9,19 @@ import ( type ContainerEngine interface { ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error - ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) - ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) + ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, 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) (int, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) - ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error + ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) - ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error) + ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) + ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) @@ -29,9 +30,9 @@ type ContainerEngine interface { ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) - PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) + PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error) @@ -41,8 +42,6 @@ type ContainerEngine interface { PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) - PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) - VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index a28bfc548..16b96e9ef 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -6,16 +6,17 @@ import ( type ImageEngine interface { Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, 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) Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error) List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) + Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) - Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error - Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error - Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) - Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error + Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error + Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error } diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index dd7aaa07f..7e35957f4 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -4,6 +4,7 @@ import ( "net" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -49,3 +50,15 @@ type InspectOptions struct { Latest bool `json:",omitempty"` Size bool `json:",omitempty"` } + +// 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 +} + +// DiffReport provides changes for object +type DiffReport struct { + Changes []archive.Change +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 828ee56f0..ccbe6d4fd 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -599,9 +599,9 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri ExitCode: 125, } if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { - //if lastError != nil { + // if lastError != nil { // fmt.Fprintln(os.Stderr, lastError) - //} + // } report.Err = err if errors.Cause(err) == define.ErrWillDeadlock { report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") @@ -623,6 +623,19 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C return ps.GetContainerLists(ic.Libpod, options) } +// ContainerDiff provides changes to given container +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, opts entities.DiffOptions) (*entities.DiffReport, error) { + if opts.Latest { + ctnr, err := ic.Libpod.GetLatestContainer() + if err != nil { + return nil, errors.Wrap(err, "unable to get latest container") + } + nameOrId = ctnr.ID() + } + changes, err := ic.Libpod.GetDiff("", nameOrId) + return &entities.DiffReport{Changes: changes}, err +} + func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { var ( joinPod bool diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 9d706a112..2edef2723 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -413,3 +413,11 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress) } + +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 +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index e96200c5b..b22c6e3ba 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -324,3 +324,8 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { return nil, errors.New("not implemented") } + +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := containers.Diff(ic.ClientCxt, nameOrId) + return &entities.DiffReport{Changes: changes}, err +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 516914a68..66abd7f47 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -241,3 +241,12 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return utils2.UntarToFileSystem(options.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) { + changes, err := images.Diff(ir.ClientCxt, nameOrId) + if err != nil { + return nil, err + } + return &entities.DiffReport{Changes: changes}, nil +} -- cgit v1.2.3-54-g00ecf