move driver and image metadata to libkpod
Signed-off-by: Ryan Cole <rcyoalne@gmail.com>
This commit is contained in:
parent
a95bbe9608
commit
95e17b4a73
10 changed files with 81 additions and 903 deletions
|
@ -1,29 +1,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cp "github.com/containers/image/copy"
|
||||
"github.com/containers/image/signature"
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type imageMetadata struct {
|
||||
Tag string `json:"tag"`
|
||||
CreatedTime time.Time `json:"created-time"`
|
||||
ID string `json:"id"`
|
||||
Blobs []types.BlobInfo `json:"blob-list"`
|
||||
Layers map[string][]string `json:"layers"`
|
||||
SignatureSizes []string `json:"signature-sizes"`
|
||||
}
|
||||
|
||||
// DockerRegistryOptions encapsulates settings that affect how we connect or
|
||||
// authenticate to a remote registry.
|
||||
type dockerRegistryOptions struct {
|
||||
|
@ -77,16 +67,6 @@ func getStore(c *cli.Context) (storage.Store, error) {
|
|||
return store, nil
|
||||
}
|
||||
|
||||
func parseMetadata(image storage.Image) (imageMetadata, error) {
|
||||
var im imageMetadata
|
||||
|
||||
dec := json.NewDecoder(strings.NewReader(image.Metadata))
|
||||
if err := dec.Decode(&im); err != nil {
|
||||
return imageMetadata{}, err
|
||||
}
|
||||
return im, nil
|
||||
}
|
||||
|
||||
func findImage(store storage.Store, image string) (*storage.Image, error) {
|
||||
var img *storage.Image
|
||||
ref, err := is.Transport.ParseStoreReference(store, image)
|
||||
|
@ -239,41 +219,25 @@ func isValidBool(str string) bool {
|
|||
return isTrue(str) || isFalse(str)
|
||||
}
|
||||
|
||||
func getDriverName(store storage.Store) (string, error) {
|
||||
driver, err := store.GraphDriver()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return driver.String(), nil
|
||||
}
|
||||
|
||||
func getDriverMetadata(store storage.Store, layerID string) (map[string]string, error) {
|
||||
driver, err := store.GraphDriver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return driver.Metadata(layerID)
|
||||
}
|
||||
|
||||
func getImageSize(image storage.Image, store storage.Store) (int64, error) {
|
||||
func getImageSize(img storage.Image, store storage.Store) (int64, error) {
|
||||
is.Transport.SetStore(store)
|
||||
storeRef, err := is.Transport.ParseStoreReference(store, "@"+image.ID)
|
||||
storeRef, err := is.Transport.ParseStoreReference(store, "@"+img.ID)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
img, err := storeRef.NewImage(nil)
|
||||
imgRef, err := storeRef.NewImage(nil)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
imgSize, err := img.Size()
|
||||
imgSize, err := imgRef.Size()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return imgSize, nil
|
||||
}
|
||||
|
||||
func getImageTopLayer(image storage.Image) (string, error) {
|
||||
metadata, err := parseMetadata(image)
|
||||
func getImageTopLayer(img storage.Image) (string, error) {
|
||||
metadata, err := image.ParseMetadata(img)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -30,30 +30,6 @@ func TestGetStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
} else if len(images) == 0 {
|
||||
t.Fatalf("no images with metadata to parse")
|
||||
}
|
||||
|
||||
_, err = parseMetadata(images[0])
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSize(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/cmd/kpod/docker"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/driver"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/annotations"
|
||||
"github.com/kubernetes-incubator/cri-o/server"
|
||||
|
@ -71,7 +72,7 @@ func getContainerData(store storage.Store, name string, size bool) (*containerDa
|
|||
return nil, err
|
||||
}
|
||||
|
||||
driverName, err := getDriverName(store)
|
||||
driverName, err := driver.GetDriverName(store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -79,7 +80,7 @@ func getContainerData(store storage.Store, name string, size bool) (*containerDa
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
driverMetadata, err := getDriverMetadata(store, topLayer)
|
||||
driverMetadata, err := driver.GetDriverMetadata(store, topLayer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/driver"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -94,7 +96,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) {
|
|||
}
|
||||
}
|
||||
|
||||
driverName, err := getDriverName(store)
|
||||
driverName, err := driver.GetDriverName(store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -103,7 +105,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
driverMetadata, err := getDriverMetadata(store, topLayerID)
|
||||
driverMetadata, err := driver.GetDriverMetadata(store, topLayerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -150,7 +152,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) {
|
|||
}
|
||||
|
||||
func getDigests(img storage.Image) ([]digest.Digest, error) {
|
||||
metadata, err := parseMetadata(img)
|
||||
metadata, err := image.ParseMetadata(img)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -190,8 +191,8 @@ func outputHeader(truncate, digests bool) {
|
|||
}
|
||||
|
||||
func outputImages(images []storage.Image, format string, store storage.Store, filters *filterParams, argName string, hasTemplate, truncate, digests, quiet bool) error {
|
||||
for _, image := range images {
|
||||
imageMetadata, err := parseMetadata(image)
|
||||
for _, img := range images {
|
||||
imageMetadata, err := image.ParseMetadata(img)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
@ -200,27 +201,27 @@ func outputImages(images []storage.Image, format string, store storage.Store, fi
|
|||
if len(imageMetadata.Blobs) > 0 {
|
||||
digest = string(imageMetadata.Blobs[0].Digest)
|
||||
}
|
||||
size, _ := getImageSize(image, store)
|
||||
size, _ := getImageSize(img, store)
|
||||
|
||||
names := []string{""}
|
||||
if len(image.Names) > 0 {
|
||||
names = image.Names
|
||||
if len(img.Names) > 0 {
|
||||
names = img.Names
|
||||
} else {
|
||||
// images without names should be printed with "<none>" as the image name
|
||||
names = append(names, "<none>")
|
||||
}
|
||||
for _, name := range names {
|
||||
if !matchesFilter(image, store, name, filters) || !matchesReference(name, argName) {
|
||||
if !matchesFilter(img, store, name, filters) || !matchesReference(name, argName) {
|
||||
continue
|
||||
}
|
||||
if quiet {
|
||||
fmt.Printf("%-64s\n", image.ID)
|
||||
fmt.Printf("%-64s\n", img.ID)
|
||||
// We only want to print each id once
|
||||
break
|
||||
}
|
||||
|
||||
params := imageOutputParams{
|
||||
ID: image.ID,
|
||||
ID: img.ID,
|
||||
Name: name,
|
||||
Digest: digest,
|
||||
CreatedAt: createdTime,
|
||||
|
|
|
@ -1,678 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/storage"
|
||||
)
|
||||
|
||||
func TestTemplateOutputBlankTemplate(t *testing.T) {
|
||||
params := imageOutputParams{
|
||||
ID: "0123456789abcdef",
|
||||
Name: "test/image:latest",
|
||||
Digest: "sha256:012345789abcdef012345789abcdef012345789abcdef012345789abcdef",
|
||||
CreatedAt: "Jan 01 2016 10:45",
|
||||
Size: "97 KB",
|
||||
}
|
||||
|
||||
err := outputUsingTemplate("", params)
|
||||
//Output: Words
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateOutputValidTemplate(t *testing.T) {
|
||||
params := imageOutputParams{
|
||||
ID: "0123456789abcdef",
|
||||
Name: "test/image:latest",
|
||||
Digest: "sha256:012345789abcdef012345789abcdef012345789abcdef012345789abcdef",
|
||||
CreatedAt: "Jan 01 2016 10:45",
|
||||
Size: "97 KB",
|
||||
}
|
||||
|
||||
templateString := "{{.ID}}"
|
||||
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputUsingTemplate(templateString, params)
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(params.ID) {
|
||||
t.Errorf("Error with template output:\nExpected: %s\nReceived: %s\n", params.ID, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatStringOutput(t *testing.T) {
|
||||
params := imageOutputParams{
|
||||
ID: "012345789abcdef",
|
||||
Name: "test/image:latest",
|
||||
Digest: "sha256:012345789abcdef012345789abcdef012345789abcdef012345789abcdef",
|
||||
CreatedAt: "Jan 01 2016 10:45",
|
||||
Size: "97 KB",
|
||||
}
|
||||
|
||||
output := captureOutput(func() {
|
||||
outputUsingFormatString(true, true, params)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-12.12s %-40s %-64s %-22s %s\n", params.ID, params.Name, params.Digest, params.CreatedAt, params.Size)
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Error outputting using format string:\n\texpected: %s\n\treceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeFormatting(t *testing.T) {
|
||||
size := formattedSize(0)
|
||||
if size != "0 B" {
|
||||
t.Errorf("Error formatting size: expected '%s' got '%s'", "0 B", size)
|
||||
}
|
||||
|
||||
size = formattedSize(1024)
|
||||
if size != "1 KB" {
|
||||
t.Errorf("Error formatting size: expected '%s' got '%s'", "1 KB", size)
|
||||
}
|
||||
|
||||
size = formattedSize(1024 * 1024 * 1024 * 1024 * 1024)
|
||||
if size != "1024 TB" {
|
||||
t.Errorf("Error formatting size: expected '%s' got '%s'", "1024 TB", size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputHeader(t *testing.T) {
|
||||
output := captureOutput(func() {
|
||||
outputHeader(true, false)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-12s %-40s %-22s %s\n", "IMAGE ID", "IMAGE NAME", "CREATED AT", "SIZE")
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Error outputting header:\n\texpected: %s\n\treceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
|
||||
output = captureOutput(func() {
|
||||
outputHeader(true, true)
|
||||
})
|
||||
expectedOutput = fmt.Sprintf("%-12s %-40s %-64s %-22s %s\n", "IMAGE ID", "IMAGE NAME", "DIGEST", "CREATED AT", "SIZE")
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Error outputting header:\n\texpected: %s\n\treceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
|
||||
output = captureOutput(func() {
|
||||
outputHeader(false, false)
|
||||
})
|
||||
expectedOutput = fmt.Sprintf("%-64s %-40s %-22s %s\n", "IMAGE ID", "IMAGE NAME", "CREATED AT", "SIZE")
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Error outputting header:\n\texpected: %s\n\treceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchWithTag(t *testing.T) {
|
||||
isMatch := matchesReference("docker.io/kubernetes/pause:latest", "pause:latest")
|
||||
if !isMatch {
|
||||
t.Error("expected match, got not match")
|
||||
}
|
||||
|
||||
isMatch = matchesReference("docker.io/kubernetes/pause:latest", "kubernetes/pause:latest")
|
||||
if !isMatch {
|
||||
t.Error("expected match, got no match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoMatchesReferenceWithTag(t *testing.T) {
|
||||
isMatch := matchesReference("docker.io/kubernetes/pause:latest", "redis:latest")
|
||||
if isMatch {
|
||||
t.Error("expected no match, got match")
|
||||
}
|
||||
|
||||
isMatch = matchesReference("docker.io/kubernetes/pause:latest", "kubernetes/redis:latest")
|
||||
if isMatch {
|
||||
t.Error("expected no match, got match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesReferenceWithoutTag(t *testing.T) {
|
||||
isMatch := matchesReference("docker.io/kubernetes/pause:latest", "pause")
|
||||
if !isMatch {
|
||||
t.Error("expected match, got not match")
|
||||
}
|
||||
|
||||
isMatch = matchesReference("docker.io/kubernetes/pause:latest", "kubernetes/pause")
|
||||
if !isMatch {
|
||||
t.Error("expected match, got no match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoMatchesReferenceWithoutTag(t *testing.T) {
|
||||
isMatch := matchesReference("docker.io/kubernetes/pause:latest", "redis")
|
||||
if isMatch {
|
||||
t.Error("expected no match, got match")
|
||||
}
|
||||
|
||||
isMatch = matchesReference("docker.io/kubernetes/pause:latest", "kubernetes/redis")
|
||||
if isMatch {
|
||||
t.Error("expected no match, got match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputImagesQuietTruncated(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests quiet and truncated output
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:1], "", store, nil, "", false, true, false, true)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-64s\n", images[0].ID)
|
||||
if err != nil {
|
||||
t.Error("quiet/truncated output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Errorf("quiet/truncated output does not match expected value\nExpected: %s\nReceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputImagesQuietNotTruncated(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests quiet and non-truncated output
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:1], "", store, nil, "", false, false, false, true)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-64s\n", images[0].ID)
|
||||
if err != nil {
|
||||
t.Error("quiet/non-truncated output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Errorf("quiet/non-truncated output does not match expected value\nExpected: %s\nReceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputImagesFormatString(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests output with format template
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:1], "{{.ID}}", store, nil, "", true, true, false, false)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%s", images[0].ID)
|
||||
if err != nil {
|
||||
t.Error("format string output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Errorf("format string output does not match expected value\nExpected: %s\nReceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputImagesFormatTemplate(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests quiet and non-truncated output
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:1], "", store, nil, "", false, false, false, true)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-64s\n", images[0].ID)
|
||||
if err != nil {
|
||||
t.Error("format template output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Errorf("format template output does not match expected value\nExpected: %s\nReceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputImagesArgNoMatch(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests output with an arg name that does not match. Args ending in ":" cannot match
|
||||
// because all images in the repository must have a tag, and here the tag is an
|
||||
// empty string
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:1], "", store, nil, "foo:", false, true, false, false)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("")
|
||||
if err != nil {
|
||||
t.Error("arg no match output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Error("arg no match output should be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputMultipleImages(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// Tests quiet and truncated output
|
||||
output, err := captureOutputWithError(func() error {
|
||||
return outputImages(images[:2], "", store, nil, "", false, true, false, true)
|
||||
})
|
||||
expectedOutput := fmt.Sprintf("%-64s\n%-64s\n", images[0].ID, images[1].ID)
|
||||
if err != nil {
|
||||
t.Error("multi-image output produces error")
|
||||
} else if strings.TrimSpace(output) != strings.TrimSpace(expectedOutput) {
|
||||
t.Errorf("multi-image output does not match expected value\nExpected: %s\nReceived: %s\n", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFilterAllParams(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
label := "dangling=true,label=a=b,before=busybox:latest,since=busybox:latest,reference=abcdef"
|
||||
params, err := parseFilter(images, label)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing filter")
|
||||
}
|
||||
|
||||
expectedParams := &filterParams{dangling: "true", label: "a=b", beforeImage: "busybox:latest", sinceImage: "busybox:latest", referencePattern: "abcdef"}
|
||||
if *params != *expectedParams {
|
||||
t.Errorf("filter did not return expected result\n\tExpected: %v\n\tReceived: %v", expectedParams, params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFilterInvalidDangling(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
label := "dangling=NO,label=a=b,before=busybox:latest,since=busybox:latest,reference=abcdef"
|
||||
_, err = parseFilter(images, label)
|
||||
if err == nil || err.Error() != "invalid filter: 'dangling=[NO]'" {
|
||||
t.Fatalf("expected error parsing filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFilterInvalidBefore(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
label := "dangling=false,label=a=b,before=:,since=busybox:latest,reference=abcdef"
|
||||
_, err = parseFilter(images, label)
|
||||
if err == nil || !strings.Contains(err.Error(), "no such id") {
|
||||
t.Fatalf("expected error parsing filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFilterInvalidSince(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
label := "dangling=false,label=a=b,before=busybox:latest,since=:,reference=abcdef"
|
||||
_, err = parseFilter(images, label)
|
||||
if err == nil || !strings.Contains(err.Error(), "no such id") {
|
||||
t.Fatalf("expected error parsing filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFilterInvalidFilter(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
label := "foo=bar"
|
||||
_, err = parseFilter(images, label)
|
||||
if err == nil || err.Error() != "invalid filter: 'foo'" {
|
||||
t.Fatalf("expected error parsing filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageExistsTrue(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:katest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
|
||||
if !imageExists(images, "busybox:latest") {
|
||||
t.Errorf("expected image %s to exist", "busybox:latest")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageExistsFalse(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
if imageExists(images, ":") {
|
||||
t.Errorf("image %s should not exist", ":")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesDangingTrue(t *testing.T) {
|
||||
if !matchesDangling("<none>", "true") {
|
||||
t.Error("matchesDangling() should return true with dangling=true and name=<none>")
|
||||
}
|
||||
|
||||
if !matchesDangling("hello", "false") {
|
||||
t.Error("matchesDangling() should return true with dangling=false and name='hello'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesDangingFalse(t *testing.T) {
|
||||
if matchesDangling("hello", "true") {
|
||||
t.Error("matchesDangling() should return false with dangling=true and name=hello")
|
||||
}
|
||||
|
||||
if matchesDangling("<none>", "false") {
|
||||
t.Error("matchesDangling() should return false with dangling=false and name=<none>")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesLabelTrue(t *testing.T) {
|
||||
//TODO: How do I implement this?
|
||||
}
|
||||
|
||||
func TestMatchesLabelFalse(t *testing.T) {
|
||||
// TODO: How do I implement this?
|
||||
}
|
||||
|
||||
func TestMatchesBeforeImageTrue(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// by default, params.seenImage is false
|
||||
params := new(filterParams)
|
||||
params.seenImage = false
|
||||
params.beforeImage = "foo:bar"
|
||||
if !matchesBeforeImage(images[0], ":", params) {
|
||||
t.Error("should have matched beforeImage")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesBeforeImageFalse(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// by default, params.seenImage is false
|
||||
params := new(filterParams)
|
||||
params.seenImage = true
|
||||
params.beforeImage = "foo:bar"
|
||||
// Should return false because the image has been seen
|
||||
if matchesBeforeImage(images[0], ":", params) {
|
||||
t.Error("should not have matched beforeImage")
|
||||
}
|
||||
|
||||
params.seenImage = false
|
||||
if matchesBeforeImage(images[0], "foo:bar", params) {
|
||||
t.Error("image should have been filtered out")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesSinceeImageTrue(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// by default, params.seenImage is false
|
||||
params := new(filterParams)
|
||||
params.seenImage = true
|
||||
params.sinceImage = "foo:bar"
|
||||
if !matchesSinceImage(images[0], ":", params) {
|
||||
t.Error("should have matched SinceImage")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchesSinceImageFalse(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
store, err := storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
|
||||
// by default, params.seenImage is false
|
||||
params := new(filterParams)
|
||||
params.seenImage = false
|
||||
params.sinceImage = "foo:bar"
|
||||
// Should return false because the image has been seen
|
||||
if matchesSinceImage(images[0], ":", params) {
|
||||
t.Error("should not have matched sinceImage")
|
||||
}
|
||||
|
||||
if matchesSinceImage(images[0], "foo:bar", params) {
|
||||
t.Error("image should have been filtered out")
|
||||
}
|
||||
}
|
||||
|
||||
func captureOutputWithError(f func() error) (string, error) {
|
||||
old := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
err := f()
|
||||
|
||||
w.Close()
|
||||
os.Stdout = old
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, r)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
// Captures output so that it can be compared to expected values
|
||||
func captureOutput(f func()) string {
|
||||
old := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
f()
|
||||
|
||||
w.Close()
|
||||
os.Stdout = old
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, r)
|
||||
return buf.String()
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/storage"
|
||||
)
|
||||
|
||||
func TestProperImageRefTrue(t *testing.T) {
|
||||
// Pull an image so we know we have it
|
||||
err := pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove")
|
||||
}
|
||||
// This should match a url path
|
||||
imgRef, err := properImageRef("docker://busybox:latest")
|
||||
if err != nil {
|
||||
t.Errorf("could not match image: %v", err)
|
||||
} else if imgRef == nil {
|
||||
t.Error("Returned nil Image Reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProperImageRefFalse(t *testing.T) {
|
||||
// Pull an image so we know we have it
|
||||
err := pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatal("could not pull image to remove")
|
||||
}
|
||||
// This should match a url path
|
||||
imgRef, _ := properImageRef("docker://:")
|
||||
if imgRef != nil {
|
||||
t.Error("should not have found an Image Reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageImageRefTrue(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
options := storage.DefaultStoreOptions
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not get store: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
imgRef, err := storageImageRef(store, "busybox")
|
||||
if err != nil {
|
||||
t.Errorf("could not match image: %v", err)
|
||||
} else if imgRef == nil {
|
||||
t.Error("Returned nil Image Reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageImageRefFalse(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
options := storage.DefaultStoreOptions
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not get store: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
imgRef, _ := storageImageRef(store, "")
|
||||
if imgRef != nil {
|
||||
t.Error("should not have found an Image Reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageImageIDTrue(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
options := storage.DefaultStoreOptions
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not get store: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
err = pullTestImage("busybox:latest")
|
||||
if err != nil {
|
||||
t.Fatalf("could not pull image to remove: %v", err)
|
||||
}
|
||||
//Somehow I have to get the id of the image I just pulled
|
||||
images, err := store.Images()
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading images: %v", err)
|
||||
}
|
||||
id, err := captureOutputWithError(func() error {
|
||||
return outputImages(images, "", store, nil, "busybox:latest", false, false, false, true)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting id of image: %v", err)
|
||||
}
|
||||
id = strings.TrimSpace(id)
|
||||
|
||||
imgRef, err := storageImageID(store, id)
|
||||
if err != nil {
|
||||
t.Errorf("could not match image: %v", err)
|
||||
} else if imgRef == nil {
|
||||
t.Error("Returned nil Image Reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageImageIDFalse(t *testing.T) {
|
||||
// Make sure the tests are running as root
|
||||
failTestIfNotRoot(t)
|
||||
|
||||
options := storage.DefaultStoreOptions
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not get store: %v", err)
|
||||
}
|
||||
// Pull an image so we know we have it
|
||||
|
||||
id := ""
|
||||
|
||||
imgRef, _ := storageImageID(store, id)
|
||||
if imgRef != nil {
|
||||
t.Error("should not have returned Image Reference")
|
||||
}
|
||||
}
|
24
libkpod/driver/driver.go
Normal file
24
libkpod/driver/driver.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package driver
|
||||
|
||||
import cstorage "github.com/containers/storage"
|
||||
|
||||
type DriverData struct {
|
||||
Name string
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
func GetDriverName(store cstorage.Store) (string, error) {
|
||||
driver, err := store.GraphDriver()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return driver.String(), nil
|
||||
}
|
||||
|
||||
func GetDriverMetadata(store cstorage.Store, layerID string) (map[string]string, error) {
|
||||
driver, err := store.GraphDriver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return driver.Metadata(layerID)
|
||||
}
|
1
libkpod/image/image.go
Normal file
1
libkpod/image/image.go
Normal file
|
@ -0,0 +1 @@
|
|||
package image
|
32
libkpod/image/metadata.go
Normal file
32
libkpod/image/metadata.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
)
|
||||
|
||||
// Metadata stores all of the metadata for an image
|
||||
type Metadata struct {
|
||||
Tag string `json:"tag"`
|
||||
CreatedTime time.Time `json:"created-time"`
|
||||
ID string `json:"id"`
|
||||
Blobs []types.BlobInfo `json:"blob-list"`
|
||||
Layers map[string][]string `json:"layers"`
|
||||
SignatureSizes []string `json:"signature-sizes"`
|
||||
}
|
||||
|
||||
// ParseMetadata takes an image, parses the json stored in it's metadata
|
||||
// field, and converts it to a Metadata struct
|
||||
func ParseMetadata(image storage.Image) (Metadata, error) {
|
||||
var m Metadata
|
||||
|
||||
dec := json.NewDecoder(strings.NewReader(image.Metadata))
|
||||
if err := dec.Decode(&m); err != nil {
|
||||
return Metadata{}, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
Loading…
Reference in a new issue