commit
2bdcd460bb
6 changed files with 238 additions and 0 deletions
89
cmd/kpod/diff.go
Normal file
89
cmd/kpod/diff.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
type diffJSONOutput struct {
|
||||||
|
Changed []string `json:"changed,omitempty"`
|
||||||
|
Added []string `json:"added,omitempty"`
|
||||||
|
Deleted []string `json:"deleted,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
diffFlags = []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "archive",
|
||||||
|
Usage: "Save the diff as a tar archive",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "json",
|
||||||
|
Usage: "Format output as JSON",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem. The
|
||||||
|
container or image will be compared to its parent layer`)
|
||||||
|
|
||||||
|
diffCommand = cli.Command{
|
||||||
|
Name: "diff",
|
||||||
|
Usage: "Inspect changes on container's file systems",
|
||||||
|
Description: diffDescription,
|
||||||
|
Flags: diffFlags,
|
||||||
|
Action: diffCmd,
|
||||||
|
ArgsUsage: "ID-NAME",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func diffCmd(c *cli.Context) error {
|
||||||
|
if len(c.Args()) != 1 {
|
||||||
|
return errors.Errorf("container, layer, or image name must be specified: kpod diff [options [...]] ID-NAME")
|
||||||
|
}
|
||||||
|
config, err := getConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get config")
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := libkpod.New(config)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get container server")
|
||||||
|
}
|
||||||
|
|
||||||
|
to := c.Args().Get(0)
|
||||||
|
changes, err := server.GetDiff("", to)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get changes for %q", to)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool("json") {
|
||||||
|
jsonStruct := diffJSONOutput{}
|
||||||
|
for _, change := range changes {
|
||||||
|
if change.Kind == archive.ChangeModify {
|
||||||
|
jsonStruct.Changed = append(jsonStruct.Changed, change.Path)
|
||||||
|
} else if change.Kind == archive.ChangeAdd {
|
||||||
|
jsonStruct.Added = append(jsonStruct.Added, change.Path)
|
||||||
|
} else if change.Kind == archive.ChangeDelete {
|
||||||
|
jsonStruct.Deleted = append(jsonStruct.Deleted, change.Path)
|
||||||
|
} else {
|
||||||
|
return errors.Errorf("change kind %q not recognized", change.Kind.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b, err := json.MarshalIndent(jsonStruct, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not marshal json for %+v", jsonStruct)
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
} else {
|
||||||
|
for _, change := range changes {
|
||||||
|
fmt.Println(change.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ func main() {
|
||||||
app.Version = Version
|
app.Version = Version
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
|
diffCommand,
|
||||||
exportCommand,
|
exportCommand,
|
||||||
historyCommand,
|
historyCommand,
|
||||||
imagesCommand,
|
imagesCommand,
|
||||||
|
|
45
docs/kpod-diff.1.md
Normal file
45
docs/kpod-diff.1.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
% kpod(1) kpod-diff - Inspect changes on a container or image's filesystem
|
||||||
|
% Dan Walsh
|
||||||
|
# kpod-diff "1" "August 2017" "kpod"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
kpod diff - Inspect changes on a container or image's filesystem
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**kpod** **diff** [*options* [...]] NAME
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
**--json**
|
||||||
|
|
||||||
|
Format output as json
|
||||||
|
|
||||||
|
|
||||||
|
## EXAMPLE
|
||||||
|
|
||||||
|
kpod diff redis:alpine
|
||||||
|
C /usr
|
||||||
|
C /usr/local
|
||||||
|
C /usr/local/bin
|
||||||
|
A /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
kpod diff --json redis:alpine
|
||||||
|
{
|
||||||
|
"changed": [
|
||||||
|
"/usr",
|
||||||
|
"/usr/local",
|
||||||
|
"/usr/local/bin"
|
||||||
|
],
|
||||||
|
"added": [
|
||||||
|
"/usr/local/bin/docker-entrypoint.sh"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
kpod(1)
|
||||||
|
|
||||||
|
## HISTORY
|
||||||
|
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
|
54
libkpod/diff.go
Normal file
54
libkpod/diff.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package libkpod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libkpod/layer"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDiff returns the differences betwen the two images, layers, or containers
|
||||||
|
func (c *ContainerServer) GetDiff(from, to string) ([]archive.Change, error) {
|
||||||
|
toLayer, err := c.getLayerID(to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fromLayer := ""
|
||||||
|
if from != "" {
|
||||||
|
fromLayer, err = c.getLayerID(from)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Store().Changes(fromLayer, toLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLayerID gets a full layer id given a full or partial id
|
||||||
|
// If the id matches a container or image, the id of the top layer is returned
|
||||||
|
// If the id matches a layer, the top layer id is returned
|
||||||
|
func (c *ContainerServer) getLayerID(id string) (string, error) {
|
||||||
|
var toLayer string
|
||||||
|
toImage, err := image.FindImage(c.store, id)
|
||||||
|
if err != nil {
|
||||||
|
toCtr, err := c.store.Container(id)
|
||||||
|
if err != nil {
|
||||||
|
toLayer, err = layer.FullID(c.store, id)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Errorf("layer, image, or container %s does not exist", id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toLayer = toCtr.LayerID
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toLayer = toImage.TopLayer
|
||||||
|
}
|
||||||
|
return toLayer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ContainerServer) getLayerParent(layerID string) (string, error) {
|
||||||
|
layer, err := c.store.Layer(layerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return layer.Parent, nil
|
||||||
|
}
|
12
libkpod/layer/layer.go
Normal file
12
libkpod/layer/layer.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package layer
|
||||||
|
|
||||||
|
import cstorage "github.com/containers/storage"
|
||||||
|
|
||||||
|
// FullID gets the full id of a layer given a partial id or name
|
||||||
|
func FullID(store cstorage.Store, id string) (string, error) {
|
||||||
|
layer, err := store.Layer(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return layer.ID, nil
|
||||||
|
}
|
37
test/kpod_diff.bats
Normal file
37
test/kpod_diff.bats
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
IMAGE="alpine:latest"
|
||||||
|
ROOT="$TESTDIR/crio"
|
||||||
|
RUNROOT="$TESTDIR/crio-run"
|
||||||
|
KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT --storage-driver vfs"
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
cleanup_test
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test diff of image and parent" {
|
||||||
|
run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} $KPOD_OPTIONS diff $IMAGE
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
echo "$output"
|
||||||
|
run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test diff on non-existent layer" {
|
||||||
|
run ${KPOD_BINARY} $KPOD_OPTIONS diff "abc123"
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test diff with json output" {
|
||||||
|
run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
# run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} diff --json $IMAGE | python -m json.tool"
|
||||||
|
run ${KPOD_BINARY} $KPOD_OPTIONS diff --json $IMAGE
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
echo "$output"
|
||||||
|
run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
||||||
|
}
|
Loading…
Reference in a new issue