Add and use bin2img for creating images for tests

Add tests which exercise image pulling, listing, and removal.  When running
tests, prepopulate the store with an image with the default infrastructure
container's name, using the locally-built "pause" binary, so that tests won't
have to pull it down from the network.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai 2016-12-16 18:34:51 -05:00
parent c0333b102b
commit 636d5d8e9a
6 changed files with 337 additions and 1 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ pause/pause.o
ocid.conf
*.orig
*.rej
test/bin2img/bin2img

View file

@ -44,6 +44,9 @@ conmon:
pause:
make -C $@
bin2img:
make -C test/$@
ocid:
ifndef GOPATH
$(error GOPATH is not set)
@ -73,6 +76,7 @@ clean:
find . -name \#\* -delete
make -C conmon clean
make -C pause clean
make -C test/bin2img clean
ocidimage:
docker build -t ${OCID_IMAGE} .
@ -86,7 +90,7 @@ integration: ocidimage
localintegration: binaries
./test/test_runner.sh ${TESTFLAGS}
binaries: ocid ocic kpod conmon pause
binaries: ocid ocic kpod conmon pause bin2img
MANPAGES_MD := $(wildcard docs/*.md)
MANPAGES := $(MANPAGES_MD:%.md=%)
@ -180,6 +184,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man
go get -u github.com/cpuguy83/go-md2man
.PHONY: \
bin2img \
binaries \
clean \
conmon \

6
test/bin2img/Makefile Normal file
View file

@ -0,0 +1,6 @@
bin2img: $(wildcard *.go)
go build -o $@
.PHONY: clean
clean:
rm -f bin2img

225
test/bin2img/bin2img.go Normal file
View file

@ -0,0 +1,225 @@
package main
import (
"archive/tar"
"bytes"
"encoding/json"
"io"
"os"
"runtime"
"github.com/Sirupsen/logrus"
"github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/storage/pkg/reexec"
sstorage "github.com/containers/storage/storage"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli"
)
func main() {
if reexec.Init() {
return
}
app := cli.NewApp()
app.Name = "bin2img"
app.Usage = "barebones image builder"
app.Version = "0.0.1"
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug",
Usage: "turn on debug logging",
},
cli.StringFlag{
Name: "root",
Usage: "graph root directory",
},
cli.StringFlag{
Name: "runroot",
Usage: "run root directory",
},
cli.StringFlag{
Name: "storage-driver",
Usage: "storage driver",
},
cli.StringSliceFlag{
Name: "storage-option",
Usage: "storage option",
},
cli.StringFlag{
Name: "image-name",
Usage: "set image name",
Value: "kubernetes/pause",
},
cli.StringFlag{
Name: "source-binary",
Usage: "source binary",
Value: "../../pause/pause",
},
cli.StringFlag{
Name: "image-binary",
Usage: "image binary",
Value: "/pause",
},
}
app.Action = func(c *cli.Context) error {
debug := c.GlobalBool("debug")
rootDir := c.GlobalString("root")
runrootDir := c.GlobalString("runroot")
storageDriver := c.GlobalString("storage-driver")
storageOptions := c.GlobalStringSlice("storage-option")
imageName := c.GlobalString("image-name")
sourceBinary := c.GlobalString("source-binary")
imageBinary := c.GlobalString("image-binary")
if debug {
logrus.SetLevel(logrus.DebugLevel)
} else {
logrus.SetLevel(logrus.ErrorLevel)
}
if rootDir == "" && runrootDir != "" {
logrus.Errorf("must set --root and --runroot, or neither")
os.Exit(1)
}
if rootDir != "" && runrootDir == "" {
logrus.Errorf("must set --root and --runroot, or neither")
os.Exit(1)
}
storeOptions := sstorage.DefaultStoreOptions
if rootDir != "" && runrootDir != "" {
storeOptions.GraphDriverName = storageDriver
storeOptions.GraphDriverOptions = storageOptions
storeOptions.GraphRoot = rootDir
storeOptions.RunRoot = runrootDir
}
store, err := sstorage.GetStore(storeOptions)
if err != nil {
logrus.Errorf("error opening storage: %v", err)
os.Exit(1)
}
defer store.Shutdown(false)
layerBuffer := &bytes.Buffer{}
binary, err := os.Open(sourceBinary)
if err != nil {
logrus.Errorf("error opening image binary: %v", err)
os.Exit(1)
}
binInfo, err := binary.Stat()
if err != nil {
logrus.Errorf("error statting image binary: %v", err)
os.Exit(1)
}
archive := tar.NewWriter(layerBuffer)
err = archive.WriteHeader(&tar.Header{
Name: imageBinary,
Size: binInfo.Size(),
Mode: 0555,
ModTime: binInfo.ModTime(),
Typeflag: tar.TypeReg,
Uname: "root",
Gname: "root",
})
if err != nil {
logrus.Errorf("error writing archive header: %v", err)
os.Exit(1)
}
_, err = io.Copy(archive, binary)
if err != nil {
logrus.Errorf("error archiving image binary: %v", err)
os.Exit(1)
}
archive.Close()
binary.Close()
layerInfo := types.BlobInfo{
Digest: digest.Canonical.FromBytes(layerBuffer.Bytes()),
Size: int64(layerBuffer.Len()),
}
ref, err := storage.Transport.ParseStoreReference(store, imageName)
if err != nil {
logrus.Errorf("error parsing image name: %v", err)
os.Exit(1)
}
img, err := ref.NewImageDestination(nil)
if err != nil {
logrus.Errorf("error preparing to write image: %v", err)
os.Exit(1)
}
defer img.Close()
layer, err := img.PutBlob(layerBuffer, layerInfo)
if err != nil {
logrus.Errorf("error preparing to write image: %v", err)
os.Exit(1)
}
config := &v1.Image{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
Config: v1.ImageConfig{
User: "root",
Entrypoint: []string{imageBinary},
},
RootFS: v1.RootFS{
Type: "layers",
DiffIDs: []string{
layer.Digest.String(),
},
},
}
cbytes, err := json.Marshal(config)
if err != nil {
logrus.Errorf("error encoding configuration: %v", err)
os.Exit(1)
}
configInfo := types.BlobInfo{
Digest: digest.Canonical.FromBytes(cbytes),
Size: int64(len(cbytes)),
}
configInfo, err = img.PutBlob(bytes.NewBuffer(cbytes), configInfo)
if err != nil {
logrus.Errorf("error saving configuration: %v", err)
os.Exit(1)
}
manifest := &v1.Manifest{
Versioned: specs.Versioned{
SchemaVersion: 2,
MediaType: v1.MediaTypeImageManifest,
},
Config: v1.Descriptor{
MediaType: v1.MediaTypeImageConfig,
Digest: configInfo.Digest.String(),
Size: int64(len(cbytes)),
},
Layers: []v1.Descriptor{{
MediaType: v1.MediaTypeImageLayer,
Digest: layer.Digest.String(),
Size: layer.Size,
}},
}
mbytes, err := json.Marshal(manifest)
if err != nil {
logrus.Errorf("error encoding manifest: %v", err)
os.Exit(1)
}
err = img.PutManifest(mbytes)
if err != nil {
logrus.Errorf("error saving manifest: %v", err)
os.Exit(1)
}
err = img.Commit()
if err != nil {
logrus.Errorf("error committing image: %v", err)
os.Exit(1)
}
return nil
}
if err := app.Run(os.Args); err != nil {
logrus.Fatal(err)
}
}

View file

@ -36,6 +36,8 @@ APPARMOR_TEST_PROFILE_NAME=${APPARMOR_TEST_PROFILE_NAME:-apparmor-test-deny-writ
BOOT_CONFIG_FILE_PATH=${BOOT_CONFIG_FILE_PATH:-/boot/config-`uname -r`}
# Path of apparmor parameters file.
APPARMOR_PARAMETERS_FILE_PATH=${APPARMOR_PARAMETERS_FILE_PATH:-/sys/module/apparmor/parameters/enabled}
# Path of the bin2img binary.
BIN2IMG_BINARY=${BIN2IMG_BINARY:-${OCID_ROOT}/cri-o/test/bin2img/bin2img}
TESTDIR=$(mktemp -d)
if [ -e /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then
@ -112,6 +114,10 @@ function start_ocid() {
apparmor="$APPARMOR_PROFILE"
fi
# Don't forget: bin2img and ocid have their own default drivers, so if you override either, you probably need to override both
if ! [ "$3" = "--no-pause-image" ] ; then
"$BIN2IMG_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --source-binary "$PAUSE_BINARY"
fi
"$OCID_BINARY" --conmon "$CONMON_BINARY" --listen "$OCID_SOCKET" --runtime "$RUNC_BINARY" --root "$TESTDIR/ocid" --runroot "$TESTDIR/ocid-run" --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$OCID_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json config >$OCID_CONFIG
"$OCID_BINARY" --debug --config "$OCID_CONFIG" & OCID_PID=$!
wait_until_reachable

93
test/image.bats Normal file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env bats
load helpers
IMAGE=kubernetes/pause
function teardown() {
cleanup_test
}
@test "image pull" {
start_ocid "" "" --no-pause-image
run ocic image pull "$IMAGE"
echo "$output"
[ "$status" -eq 0 ]
cleanup_images
stop_ocid
}
@test "image list with filter" {
start_ocid "" "" --no-pause-image
run ocic image pull "$IMAGE"
echo "$output"
[ "$status" -eq 0 ]
run ocic image list --quiet "$IMAGE"
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
run ocic image remove --id "$id"
echo "$output"
[ "$status" -eq 0 ]
done
run ocic image list --quiet
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
echo "$id"
status=1
done
cleanup_images
stop_ocid
}
@test "image list/remove" {
start_ocid "" "" --no-pause-image
run ocic image pull "$IMAGE"
echo "$output"
[ "$status" -eq 0 ]
run ocic image list --quiet
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
run ocic image remove --id "$id"
echo "$output"
[ "$status" -eq 0 ]
done
run ocic image list --quiet
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
echo "$id"
status=1
done
cleanup_images
stop_ocid
}
@test "image status/remove" {
start_ocid "" "" --no-pause-image
run ocic image pull "$IMAGE"
echo "$output"
[ "$status" -eq 0 ]
run ocic image list --quiet
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
run ocic image status --id "$id"
echo "$output"
[ "$status" -eq 0 ]
run ocic image remove --id "$id"
echo "$output"
[ "$status" -eq 0 ]
done
run ocic image list --quiet
echo "$output"
[ "$status" -eq 0 ]
printf '%s\n' "$output" | while IFS= read -r id; do
echo "$id"
status=1
done
cleanup_images
stop_ocid
}