Continue switching from libkpod to libpod
Refactored rmi, images, diff, and history. Made fixes to kpod images in the way it was handing the templates as well as printing the image names Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
parent
cfd7aec1c3
commit
356df5d18e
18 changed files with 820 additions and 837 deletions
|
@ -2,9 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -74,25 +74,22 @@ func formatJSON(output []diffOutputParams) (diffJSONOutput, error) {
|
|||
}
|
||||
|
||||
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")
|
||||
}
|
||||
if err := validateFlags(c, diffFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := getConfig(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get config")
|
||||
|
||||
if len(c.Args()) != 1 {
|
||||
return errors.Errorf("container, image, or layer name must be specified: kpod diff [options [...]] ID-NAME")
|
||||
}
|
||||
|
||||
server, err := libkpod.New(config)
|
||||
runtime, err := getRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get container server")
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer server.Shutdown()
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
to := c.Args().Get(0)
|
||||
changes, err := server.GetDiff("", to)
|
||||
changes, err := runtime.GetDiff("", to)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get changes for %q", to)
|
||||
}
|
||||
|
|
|
@ -6,12 +6,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/common"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -45,7 +42,6 @@ type historyJSONParams struct {
|
|||
|
||||
// historyOptions stores cli flag values
|
||||
type historyOptions struct {
|
||||
image string
|
||||
human bool
|
||||
noTrunc bool
|
||||
quiet bool
|
||||
|
@ -88,14 +84,12 @@ func historyCmd(c *cli.Context) error {
|
|||
if err := validateFlags(c, historyFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := getConfig(c)
|
||||
|
||||
runtime, err := getRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Could not get config")
|
||||
}
|
||||
store, err := getStore(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
format := genHistoryFormat(c.Bool("quiet"))
|
||||
if c.IsSet("format") {
|
||||
|
@ -112,13 +106,18 @@ func historyCmd(c *cli.Context) error {
|
|||
imgName := args[0]
|
||||
|
||||
opts := historyOptions{
|
||||
image: imgName,
|
||||
human: c.BoolT("human"),
|
||||
noTrunc: c.Bool("no-trunc"),
|
||||
quiet: c.Bool("quiet"),
|
||||
format: format,
|
||||
}
|
||||
return generateHistoryOutput(store, opts)
|
||||
|
||||
history, layers, imageID, err := runtime.GetHistory(imgName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting history of image %q", imgName)
|
||||
}
|
||||
|
||||
return generateHistoryOutput(history, layers, imageID, opts)
|
||||
}
|
||||
|
||||
func genHistoryFormat(quiet bool) (format string) {
|
||||
|
@ -154,33 +153,6 @@ func (h *historyTemplateParams) headerMap() map[string]string {
|
|||
return values
|
||||
}
|
||||
|
||||
// getHistory gets the history of an image and information about its layers
|
||||
func getHistory(store storage.Store, image string) ([]v1.History, []types.BlobInfo, string, error) {
|
||||
ref, err := is.Transport.ParseStoreReference(store, image)
|
||||
if err != nil {
|
||||
return nil, nil, "", errors.Wrapf(err, "error parsing reference to image %q", image)
|
||||
}
|
||||
|
||||
img, err := is.Transport.GetStoreImage(store, ref)
|
||||
if err != nil {
|
||||
return nil, nil, "", errors.Wrapf(err, "no such image %q", image)
|
||||
}
|
||||
|
||||
systemContext := common.GetSystemContext("")
|
||||
|
||||
src, err := ref.NewImage(systemContext)
|
||||
if err != nil {
|
||||
return nil, nil, "", errors.Wrapf(err, "error instantiating image %q", image)
|
||||
}
|
||||
|
||||
oci, err := src.OCIConfig()
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
return oci.History, src.LayerInfos(), img.ID, nil
|
||||
}
|
||||
|
||||
// getHistorytemplateOutput gets the modified history information to be printed in human readable format
|
||||
func getHistoryTemplateOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) (historyOutput []historyTemplateParams) {
|
||||
var (
|
||||
|
@ -251,11 +223,7 @@ func getHistoryJSONOutput(history []v1.History, layers []types.BlobInfo, imageID
|
|||
}
|
||||
|
||||
// generateHistoryOutput generates the history based on the format given
|
||||
func generateHistoryOutput(store storage.Store, opts historyOptions) error {
|
||||
history, layers, imageID, err := getHistory(store, opts.image)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting history of image %q", opts.image)
|
||||
}
|
||||
func generateHistoryOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) error {
|
||||
if len(history) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,17 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||
libpod "github.com/kubernetes-incubator/cri-o/libpod/images"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/common"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type imagesTemplateParams struct {
|
||||
ID string
|
||||
Name string
|
||||
Digest digest.Digest
|
||||
CreatedAt string
|
||||
Size string
|
||||
}
|
||||
|
||||
type imagesJSONParams struct {
|
||||
ID string `json:"id"`
|
||||
Name []string `json:"names"`
|
||||
Digest digest.Digest `json:"digest"`
|
||||
CreatedAt time.Time `json:"created"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type imagesOptions struct {
|
||||
quiet bool
|
||||
noHeading bool
|
||||
noTrunc bool
|
||||
digests bool
|
||||
format string
|
||||
}
|
||||
|
||||
var (
|
||||
imagesFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
@ -55,145 +84,88 @@ func imagesCmd(c *cli.Context) error {
|
|||
if err := validateFlags(c, imagesFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := getConfig(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Could not get config")
|
||||
}
|
||||
store, err := getStore(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
quiet := false
|
||||
if c.IsSet("quiet") {
|
||||
quiet = c.Bool("quiet")
|
||||
runtime, err := getRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Could not get runtime")
|
||||
}
|
||||
noheading := false
|
||||
if c.IsSet("noheading") {
|
||||
noheading = c.Bool("noheading")
|
||||
}
|
||||
truncate := true
|
||||
if c.IsSet("no-trunc") {
|
||||
truncate = !c.Bool("no-trunc")
|
||||
}
|
||||
digests := false
|
||||
if c.IsSet("digests") {
|
||||
digests = c.Bool("digests")
|
||||
}
|
||||
outputFormat := genImagesFormat(quiet, truncate, digests)
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
var format string
|
||||
if c.IsSet("format") {
|
||||
outputFormat = c.String("format")
|
||||
format = c.String("format")
|
||||
} else {
|
||||
format = genImagesFormat(c.Bool("quiet"), c.Bool("noheading"), c.Bool("digests"))
|
||||
}
|
||||
|
||||
name := ""
|
||||
opts := imagesOptions{
|
||||
quiet: c.Bool("quiet"),
|
||||
noHeading: c.Bool("noheading"),
|
||||
noTrunc: c.Bool("no-trunc"),
|
||||
digests: c.Bool("digests"),
|
||||
format: format,
|
||||
}
|
||||
|
||||
var imageInput string
|
||||
if len(c.Args()) == 1 {
|
||||
name = c.Args().Get(0)
|
||||
} else if len(c.Args()) > 1 {
|
||||
imageInput = c.Args().Get(0)
|
||||
}
|
||||
if len(c.Args()) > 1 {
|
||||
return errors.New("'kpod images' requires at most 1 argument")
|
||||
}
|
||||
|
||||
var params *libpod.FilterParams
|
||||
if c.IsSet("filter") {
|
||||
params, err = libpod.ParseFilter(store, c.String("filter"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing filter")
|
||||
}
|
||||
} else {
|
||||
params = nil
|
||||
params, err := runtime.ParseImageFilter(imageInput, c.String("filter"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing filter")
|
||||
}
|
||||
|
||||
imageList, err := libpod.GetImagesMatchingFilter(store, params, name)
|
||||
// generate the different filters
|
||||
labelFilter := generateImagesFilter(params, "label")
|
||||
beforeImageFilter := generateImagesFilter(params, "before-image")
|
||||
sinceImageFilter := generateImagesFilter(params, "since-image")
|
||||
danglingFilter := generateImagesFilter(params, "dangling")
|
||||
referenceFilter := generateImagesFilter(params, "reference")
|
||||
imageInputFilter := generateImagesFilter(params, "image-input")
|
||||
|
||||
images, err := runtime.GetImages(params, labelFilter, beforeImageFilter, sinceImageFilter, danglingFilter, referenceFilter, imageInputFilter)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get list of images matching filter")
|
||||
}
|
||||
|
||||
return outputImages(store, imageList, truncate, digests, quiet, outputFormat, noheading)
|
||||
return generateImagesOutput(runtime, images, opts)
|
||||
}
|
||||
|
||||
func genImagesFormat(quiet, truncate, digests bool) (format string) {
|
||||
func genImagesFormat(quiet, noHeading, digests bool) (format string) {
|
||||
if quiet {
|
||||
return formats.IDString
|
||||
}
|
||||
if truncate {
|
||||
format = "table {{ .ID | printf \"%-20.12s\" }} "
|
||||
} else {
|
||||
format = "table {{ .ID | printf \"%-64s\" }} "
|
||||
format = "table {{.ID}}\t{{.Name}}\t"
|
||||
if noHeading {
|
||||
format = "{{.ID}}\t{{.Name}}\t"
|
||||
}
|
||||
format += "{{ .Name | printf \"%-56s\" }} "
|
||||
|
||||
if digests {
|
||||
format += "{{ .Digest | printf \"%-71s \"}} "
|
||||
format += "{{.Digest}}\t"
|
||||
}
|
||||
|
||||
format += "{{ .CreatedAt | printf \"%-22s\" }} {{.Size}}"
|
||||
format += "{{.CreatedAt}}\t{{.Size}}\t"
|
||||
return
|
||||
}
|
||||
|
||||
func outputImages(store storage.Store, images []storage.Image, truncate, digests, quiet bool, outputFormat string, noheading bool) error {
|
||||
imageOutput := []imageOutputParams{}
|
||||
|
||||
lastID := ""
|
||||
for _, img := range images {
|
||||
if quiet && lastID == img.ID {
|
||||
continue // quiet should not show the same ID multiple times
|
||||
// imagesToGeneric creates an empty array of interfaces for output
|
||||
func imagesToGeneric(templParams []imagesTemplateParams, JSONParams []imagesJSONParams) (genericParams []interface{}) {
|
||||
if len(templParams) > 0 {
|
||||
for _, v := range templParams {
|
||||
genericParams = append(genericParams, interface{}(v))
|
||||
}
|
||||
createdTime := img.Created
|
||||
|
||||
names := []string{""}
|
||||
if len(img.Names) > 0 {
|
||||
names = img.Names
|
||||
}
|
||||
|
||||
info, imageDigest, size, _ := libpod.InfoAndDigestAndSize(store, img)
|
||||
if info != nil {
|
||||
createdTime = info.Created
|
||||
}
|
||||
|
||||
params := imageOutputParams{
|
||||
ID: img.ID,
|
||||
Name: names,
|
||||
Digest: imageDigest,
|
||||
CreatedAt: createdTime.Format("Jan 2, 2006 15:04"),
|
||||
Size: libpod.FormattedSize(float64(size)),
|
||||
}
|
||||
imageOutput = append(imageOutput, params)
|
||||
return
|
||||
}
|
||||
|
||||
var out formats.Writer
|
||||
|
||||
switch outputFormat {
|
||||
case formats.JSONString:
|
||||
out = formats.JSONStructArray{Output: toGeneric(imageOutput)}
|
||||
default:
|
||||
if len(imageOutput) == 0 {
|
||||
out = formats.StdoutTemplateArray{}
|
||||
} else {
|
||||
out = formats.StdoutTemplateArray{Output: toGeneric(imageOutput), Template: outputFormat, Fields: imageOutput[0].headerMap()}
|
||||
}
|
||||
for _, v := range JSONParams {
|
||||
genericParams = append(genericParams, interface{}(v))
|
||||
}
|
||||
|
||||
formats.Writer(out).Out()
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
type imageOutputParams struct {
|
||||
ID string `json:"id"`
|
||||
Name []string `json:"names"`
|
||||
Digest digest.Digest `json:"digest"`
|
||||
CreatedAt string `json:"created"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
func toGeneric(params []imageOutputParams) []interface{} {
|
||||
genericParams := make([]interface{}, len(params))
|
||||
for i, v := range params {
|
||||
genericParams[i] = interface{}(v)
|
||||
}
|
||||
return genericParams
|
||||
}
|
||||
|
||||
func (i *imageOutputParams) headerMap() map[string]string {
|
||||
// generate the header based on the template provided
|
||||
func (i *imagesTemplateParams) headerMap() map[string]string {
|
||||
v := reflect.Indirect(reflect.ValueOf(i))
|
||||
values := make(map[string]string)
|
||||
|
||||
|
@ -207,3 +179,152 @@ func (i *imageOutputParams) headerMap() map[string]string {
|
|||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// getImagesTemplateOutput returns the images information to be printed in human readable format
|
||||
func getImagesTemplateOutput(runtime *libpod.Runtime, images []*storage.Image, opts imagesOptions) (imagesOutput []imagesTemplateParams) {
|
||||
var (
|
||||
lastID string
|
||||
)
|
||||
for _, img := range images {
|
||||
if opts.quiet && lastID == img.ID {
|
||||
continue // quiet should not show the same ID multiple times
|
||||
}
|
||||
createdTime := img.Created
|
||||
|
||||
imageID := img.ID
|
||||
if !opts.noTrunc {
|
||||
imageID = imageID[:idTruncLength]
|
||||
}
|
||||
|
||||
imageName := "<none>"
|
||||
if len(img.Names) > 0 {
|
||||
imageName = img.Names[0]
|
||||
}
|
||||
|
||||
info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
|
||||
if info != nil {
|
||||
createdTime = info.Created
|
||||
}
|
||||
|
||||
params := imagesTemplateParams{
|
||||
ID: imageID,
|
||||
Name: imageName,
|
||||
Digest: imageDigest,
|
||||
CreatedAt: units.HumanDuration(time.Since((createdTime))) + " ago",
|
||||
Size: units.HumanSize(float64(size)),
|
||||
}
|
||||
imagesOutput = append(imagesOutput, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getImagesJSONOutput returns the images information in its raw form
|
||||
func getImagesJSONOutput(runtime *libpod.Runtime, images []*storage.Image) (imagesOutput []imagesJSONParams) {
|
||||
for _, img := range images {
|
||||
createdTime := img.Created
|
||||
|
||||
info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
|
||||
if info != nil {
|
||||
createdTime = info.Created
|
||||
}
|
||||
|
||||
params := imagesJSONParams{
|
||||
ID: img.ID,
|
||||
Name: img.Names,
|
||||
Digest: imageDigest,
|
||||
CreatedAt: createdTime,
|
||||
Size: size,
|
||||
}
|
||||
imagesOutput = append(imagesOutput, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// generateImagesOutput generates the images based on the format provided
|
||||
func generateImagesOutput(runtime *libpod.Runtime, images []*storage.Image, opts imagesOptions) error {
|
||||
if len(images) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out formats.Writer
|
||||
|
||||
switch opts.format {
|
||||
case formats.JSONString:
|
||||
imagesOutput := getImagesJSONOutput(runtime, images)
|
||||
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
|
||||
default:
|
||||
imagesOutput := getImagesTemplateOutput(runtime, images, opts)
|
||||
out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.format, Fields: imagesOutput[0].headerMap()}
|
||||
|
||||
}
|
||||
|
||||
return formats.Writer(out).Out()
|
||||
}
|
||||
|
||||
// generateImagesFilter returns an ImageFilter based on filterType
|
||||
// to add more filters, define a new case and write what the ImageFilter function should do
|
||||
func generateImagesFilter(params *libpod.ImageFilterParams, filterType string) libpod.ImageFilter {
|
||||
switch filterType {
|
||||
case "label":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.Label == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
pair := strings.SplitN(params.Label, "=", 2)
|
||||
if val, ok := info.Labels[pair[0]]; ok {
|
||||
if len(pair) == 2 && val == pair[1] {
|
||||
return true
|
||||
}
|
||||
if len(pair) == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
case "before-image":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.BeforeImage.IsZero() {
|
||||
return true
|
||||
}
|
||||
return info.Created.Before(params.BeforeImage)
|
||||
}
|
||||
case "since-image":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.SinceImage.IsZero() {
|
||||
return true
|
||||
}
|
||||
return info.Created.After(params.SinceImage)
|
||||
}
|
||||
case "dangling":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.Dangling == "" {
|
||||
return true
|
||||
}
|
||||
if common.IsFalse(params.Dangling) && params.ImageName != "<none>" {
|
||||
return true
|
||||
}
|
||||
if common.IsTrue(params.Dangling) && params.ImageName == "<none>" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
case "reference":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.ReferencePattern == "" {
|
||||
return true
|
||||
}
|
||||
return libpod.MatchesReference(params.ImageName, params.ReferencePattern)
|
||||
}
|
||||
case "image-input":
|
||||
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
|
||||
if params == nil || params.ImageInput == "" {
|
||||
return true
|
||||
}
|
||||
return libpod.MatchesReference(params.ImageName, params.ImageInput)
|
||||
}
|
||||
default:
|
||||
fmt.Println("invalid filter type", filterType)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/images"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -91,9 +91,9 @@ func loadCmd(c *cli.Context) error {
|
|||
output = os.Stdout
|
||||
}
|
||||
|
||||
src := images.DockerArchive + ":" + input
|
||||
src := libpod.DockerArchive + ":" + input
|
||||
if err := runtime.PullImage(src, false, "", output); err != nil {
|
||||
src = images.OCIArchive + ":" + input
|
||||
src = libpod.OCIArchive + ":" + input
|
||||
// generate full src name with specified image:tag
|
||||
if image != "" {
|
||||
src = src + ":" + image
|
||||
|
|
|
@ -3,8 +3,6 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/images"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -32,95 +30,27 @@ func rmiCmd(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
force := false
|
||||
if c.IsSet("force") {
|
||||
force = c.Bool("force")
|
||||
runtime, err := getRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
args := c.Args()
|
||||
if len(args) == 0 {
|
||||
return errors.Errorf("image name or ID must be specified")
|
||||
}
|
||||
|
||||
config, err := getConfig(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Could not get config")
|
||||
}
|
||||
store, err := getStore(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range args {
|
||||
image, err := images.FindImage(store, id)
|
||||
for _, arg := range args {
|
||||
image, err := runtime.GetImage(arg)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get image %q", id)
|
||||
return errors.Wrapf(err, "could not get image %q", arg)
|
||||
}
|
||||
if image != nil {
|
||||
ctrIDs, err := runningContainers(image, store)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting running containers for image %q", id)
|
||||
}
|
||||
if len(ctrIDs) > 0 && len(image.Names) <= 1 {
|
||||
if force {
|
||||
removeContainers(ctrIDs, store)
|
||||
} else {
|
||||
for ctrID := range ctrIDs {
|
||||
return fmt.Errorf("Could not remove image %q (must force) - container %q is using its reference image", id, ctrID)
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the user supplied an ID, we cannot delete the image if it is referred to by multiple tags
|
||||
if images.MatchesID(image.ID, id) {
|
||||
if len(image.Names) > 1 && !force {
|
||||
return fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID)
|
||||
}
|
||||
// If it is forced, we have to untag the image so that it can be deleted
|
||||
image.Names = image.Names[:0]
|
||||
} else {
|
||||
name, err2 := images.UntagImage(store, image, id)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("untagged: %s\n", name)
|
||||
}
|
||||
|
||||
if len(image.Names) > 1 {
|
||||
continue
|
||||
}
|
||||
id, err := images.RemoveImage(image, store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", id)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a list of running containers associated with the given ImageReference
|
||||
// TODO: replace this with something in libkpod
|
||||
func runningContainers(image *storage.Image, store storage.Store) ([]string, error) {
|
||||
ctrIDs := []string{}
|
||||
containers, err := store.Containers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ctr := range containers {
|
||||
if ctr.ImageID == image.ID {
|
||||
ctrIDs = append(ctrIDs, ctr.ID)
|
||||
}
|
||||
}
|
||||
return ctrIDs, nil
|
||||
}
|
||||
|
||||
// TODO: replace this with something in libkpod
|
||||
func removeContainers(ctrIDs []string, store storage.Store) error {
|
||||
for _, ctrID := range ctrIDs {
|
||||
if err := store.DeleteContainer(ctrID); err != nil {
|
||||
return errors.Wrapf(err, "could not remove container %q", ctrID)
|
||||
id, err := runtime.RemoveImage(image, c.Bool("force"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error removing image %q", id)
|
||||
}
|
||||
fmt.Printf("%s\n", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-o/libpod"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/images"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -72,12 +71,12 @@ func saveCmd(c *cli.Context) error {
|
|||
|
||||
var dst string
|
||||
switch c.String("format") {
|
||||
case images.OCIArchive:
|
||||
dst = images.OCIArchive + ":" + output
|
||||
case images.DockerArchive:
|
||||
case libpod.OCIArchive:
|
||||
dst = libpod.OCIArchive + ":" + output
|
||||
case libpod.DockerArchive:
|
||||
fallthrough
|
||||
case "":
|
||||
dst = images.DockerArchive + ":" + output
|
||||
dst = libpod.DockerArchive + ":" + output
|
||||
default:
|
||||
return errors.Errorf("unknown format option %q", c.String("format"))
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
|
||||
tm "github.com/buger/goterm"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||
"github.com/kubernetes-incubator/cri-o/libpod/images"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -182,7 +183,7 @@ func outputStatsUsingFormatString(stats *libkpod.ContainerStats) {
|
|||
}
|
||||
|
||||
func combineHumanValues(a, b uint64) string {
|
||||
return fmt.Sprintf("%s / %s", images.FormattedSize(float64(a)), images.FormattedSize(float64(b)))
|
||||
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
|
||||
}
|
||||
|
||||
func floatToPercentString(f float64) string {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue