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 <jhonce@redhat.com>
This commit is contained in:
Jhon Honce 2020-04-06 16:40:32 -07:00
parent 44f910c28c
commit 8ae28a55ac
19 changed files with 462 additions and 14 deletions

View File

@ -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)
}

74
cmd/podmanV2/diff.go Normal file
View File

@ -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])
}

View File

@ -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)
}

View File

@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra" "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 ( var (
inspectOpts *entities.InspectOptions inspectOpts *entities.InspectOptions

View File

@ -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()) 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 type podmanContextKey string
var podmanFactsKey = podmanContextKey("engineOptions") var podmanFactsKey = podmanContextKey("engineOptions")

View File

@ -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
}

View File

@ -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)
}

View File

@ -2,6 +2,7 @@ package compat
import ( import (
"github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/storage/pkg/archive"
) )
// Create container // Create container
@ -25,3 +26,12 @@ type swagCtrWaitResponse struct {
} }
} }
} }
// Object Changes
// swagger:response Changes
type swagChangesResponse struct {
// in:body
Body struct {
Changes []archive.Change
}
}

View File

@ -1377,5 +1377,40 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500: // 500:
// $ref: "#/responses/InternalError" // $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost) 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 return nil
} }

View File

@ -1125,5 +1125,36 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500: // 500:
// $ref: '#/responses/InternalError' // $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost) 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 return nil
} }

View File

@ -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)
}

View File

@ -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)
}

View File

@ -9,18 +9,19 @@ import (
type ContainerEngine interface { type ContainerEngine interface {
ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error 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) 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) 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) ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, 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 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) 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) 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) 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) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, 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) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, 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) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)
PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, 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) PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, 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) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)

View File

@ -6,16 +6,17 @@ import (
type ImageEngine interface { type ImageEngine interface {
Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error) 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) Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, 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) Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error)
List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, 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) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, 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 Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) 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
} }

View File

@ -4,6 +4,7 @@ import (
"net" "net"
"github.com/containers/libpod/pkg/specgen" "github.com/containers/libpod/pkg/specgen"
"github.com/containers/storage/pkg/archive"
"github.com/cri-o/ocicni/pkg/ocicni" "github.com/cri-o/ocicni/pkg/ocicni"
) )
@ -49,3 +50,15 @@ type InspectOptions struct {
Latest bool `json:",omitempty"` Latest bool `json:",omitempty"`
Size 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
}

View File

@ -623,6 +623,19 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C
return ps.GetContainerLists(ic.Libpod, options) 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) { func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
var ( var (
joinPod bool joinPod bool

View File

@ -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) 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
}

View File

@ -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) { func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
return nil, errors.New("not implemented") 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
}

View File

@ -241,3 +241,12 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string,
} }
return utils2.UntarToFileSystem(options.Output, f, nil) 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
}