232 lines
6.4 KiB
Go
232 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
is "github.com/containers/image/storage"
|
|
"github.com/containers/image/types"
|
|
"github.com/containers/storage"
|
|
digest "github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
"github.com/projectatomic/buildah"
|
|
"github.com/projectatomic/buildah/util"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var needToShutdownStore = false
|
|
|
|
func getStore(c *cli.Context) (storage.Store, error) {
|
|
options := storage.DefaultStoreOptions
|
|
if c.GlobalIsSet("root") || c.GlobalIsSet("runroot") {
|
|
options.GraphRoot = c.GlobalString("root")
|
|
options.RunRoot = c.GlobalString("runroot")
|
|
}
|
|
if c.GlobalIsSet("storage-driver") {
|
|
options.GraphDriverName = c.GlobalString("storage-driver")
|
|
// If any options setup in config, these should be dropped if user overrode the driver
|
|
options.GraphDriverOptions = []string{}
|
|
}
|
|
if c.GlobalIsSet("storage-opt") {
|
|
opts := c.GlobalStringSlice("storage-opt")
|
|
if len(opts) > 0 {
|
|
options.GraphDriverOptions = opts
|
|
}
|
|
}
|
|
if c.GlobalIsSet("userns-uid-map") && c.GlobalIsSet("userns-gid-map") {
|
|
uopts := c.GlobalStringSlice("userns-uid-map")
|
|
gopts := c.GlobalStringSlice("userns-gid-map")
|
|
if len(uopts) == 0 {
|
|
return nil, errors.New("--userns-uid-map used with no mappings?")
|
|
}
|
|
if len(gopts) == 0 {
|
|
return nil, errors.New("--userns-gid-map used with no mappings?")
|
|
}
|
|
uidmap, gidmap, err := util.ParseIDMappings(uopts, gopts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options.UIDMap = uidmap
|
|
options.GIDMap = gidmap
|
|
} else if c.GlobalIsSet("userns-uid-map") {
|
|
return nil, errors.Errorf("--userns-uid-map requires --userns-gid-map")
|
|
} else if c.GlobalIsSet("userns-gid-map") {
|
|
return nil, errors.Errorf("--userns-gid-map requires --userns-uid-map")
|
|
}
|
|
store, err := storage.GetStore(options)
|
|
if store != nil {
|
|
is.Transport.SetStore(store)
|
|
}
|
|
needToShutdownStore = true
|
|
return store, err
|
|
}
|
|
|
|
func openBuilder(ctx context.Context, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
|
if name != "" {
|
|
builder, err = buildah.OpenBuilder(store, name)
|
|
if os.IsNotExist(err) {
|
|
options := buildah.ImportOptions{
|
|
Container: name,
|
|
}
|
|
builder, err = buildah.ImportBuilder(ctx, store, options)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error reading build container")
|
|
}
|
|
if builder == nil {
|
|
return nil, errors.Errorf("error finding build container")
|
|
}
|
|
return builder, nil
|
|
}
|
|
|
|
func openBuilders(store storage.Store) (builders []*buildah.Builder, err error) {
|
|
return buildah.OpenAllBuilders(store)
|
|
}
|
|
|
|
func openImage(ctx context.Context, sc *types.SystemContext, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
|
options := buildah.ImportFromImageOptions{
|
|
Image: name,
|
|
SystemContext: sc,
|
|
}
|
|
builder, err = buildah.ImportBuilderFromImage(ctx, store, options)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error reading image")
|
|
}
|
|
if builder == nil {
|
|
return nil, errors.Errorf("error mocking up build configuration")
|
|
}
|
|
return builder, nil
|
|
}
|
|
|
|
func getDateAndDigestAndSize(ctx context.Context, image storage.Image, store storage.Store) (time.Time, string, int64, error) {
|
|
created := time.Time{}
|
|
is.Transport.SetStore(store)
|
|
storeRef, err := is.Transport.ParseStoreReference(store, image.ID)
|
|
if err != nil {
|
|
return created, "", -1, err
|
|
}
|
|
img, err := storeRef.NewImage(ctx, nil)
|
|
if err != nil {
|
|
return created, "", -1, err
|
|
}
|
|
defer img.Close()
|
|
imgSize, sizeErr := img.Size()
|
|
if sizeErr != nil {
|
|
imgSize = -1
|
|
}
|
|
manifest, _, manifestErr := img.Manifest(ctx)
|
|
manifestDigest := ""
|
|
if manifestErr == nil && len(manifest) > 0 {
|
|
manifestDigest = digest.Canonical.FromBytes(manifest).String()
|
|
}
|
|
inspectInfo, inspectErr := img.Inspect(ctx)
|
|
if inspectErr == nil && inspectInfo != nil {
|
|
created = *inspectInfo.Created
|
|
}
|
|
if sizeErr != nil {
|
|
err = sizeErr
|
|
} else if manifestErr != nil {
|
|
err = manifestErr
|
|
} else if inspectErr != nil {
|
|
err = inspectErr
|
|
}
|
|
return created, manifestDigest, imgSize, err
|
|
}
|
|
|
|
// getContext returns a context.TODO
|
|
func getContext() context.Context {
|
|
return context.TODO()
|
|
}
|
|
|
|
var userFlags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "user",
|
|
Usage: "`user[:group]` to run the command as",
|
|
},
|
|
}
|
|
|
|
func defaultFormat() string {
|
|
format := os.Getenv("BUILDAH_FORMAT")
|
|
if format != "" {
|
|
return format
|
|
}
|
|
return buildah.OCI
|
|
}
|
|
|
|
// imageIsParent goes through the layers in the store and checks if i.TopLayer is
|
|
// the parent of any other layer in store. Double check that image with that
|
|
// layer exists as well.
|
|
func imageIsParent(store storage.Store, topLayer string) (bool, error) {
|
|
children, err := getChildren(store, topLayer)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return len(children) > 0, nil
|
|
}
|
|
|
|
// getParent returns the image ID of the parent. Return nil if a parent is not found.
|
|
func getParent(store storage.Store, topLayer string) (*storage.Image, error) {
|
|
images, err := store.Images()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to retrieve images from store")
|
|
}
|
|
layer, err := store.Layer(topLayer)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to retrieve layers from store")
|
|
}
|
|
for _, img := range images {
|
|
if img.TopLayer == layer.Parent {
|
|
return &img, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// getChildren returns a list of the imageIDs that depend on the image
|
|
func getChildren(store storage.Store, topLayer string) ([]string, error) {
|
|
var children []string
|
|
images, err := store.Images()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to retrieve images from store")
|
|
}
|
|
layers, err := store.Layers()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to retrieve layers from store")
|
|
}
|
|
|
|
for _, layer := range layers {
|
|
if layer.Parent == topLayer {
|
|
if imageID := getImageOfTopLayer(images, layer.ID); len(imageID) > 0 {
|
|
children = append(children, imageID...)
|
|
}
|
|
}
|
|
}
|
|
return children, nil
|
|
}
|
|
|
|
// getImageOfTopLayer returns the image ID where layer is the top layer of the image
|
|
func getImageOfTopLayer(images []storage.Image, layer string) []string {
|
|
var matches []string
|
|
for _, img := range images {
|
|
if img.TopLayer == layer {
|
|
matches = append(matches, img.ID)
|
|
}
|
|
}
|
|
return matches
|
|
}
|
|
|
|
func getFormat(c *cli.Context) (string, error) {
|
|
format := strings.ToLower(c.String("format"))
|
|
if strings.HasPrefix(format, buildah.OCI) {
|
|
return buildah.OCIv1ImageManifest, nil
|
|
}
|
|
|
|
if strings.HasPrefix(format, buildah.DOCKER) {
|
|
return buildah.Dockerv2ImageManifest, nil
|
|
}
|
|
return "", errors.Errorf("unrecognized image type %q", format)
|
|
}
|