cri-o/test/bin2img/bin2img.go
Nalin Dahyabhai 636d5d8e9a 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>
2017-01-18 10:23:30 -05:00

225 lines
5.4 KiB
Go

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