diff --git a/cmd/kpod/common.go b/cmd/kpod/common.go index f4a21a27..7d3aa5b7 100644 --- a/cmd/kpod/common.go +++ b/cmd/kpod/common.go @@ -1,10 +1,8 @@ package main import ( - "io" "strings" - cp "github.com/containers/image/copy" "github.com/containers/image/signature" is "github.com/containers/image/storage" "github.com/containers/image/types" @@ -13,33 +11,6 @@ import ( "github.com/urfave/cli" ) -// DockerRegistryOptions encapsulates settings that affect how we connect or -// authenticate to a remote registry. -type dockerRegistryOptions struct { - // DockerRegistryCreds is the user name and password to supply in case - // we need to pull an image from a registry, and it requires us to - // authenticate. - DockerRegistryCreds *types.DockerAuthConfig - // DockerCertPath is the location of a directory containing CA - // certificates which will be used to verify the registry's certificate - // (all files with names ending in ".crt"), and possibly client - // certificates and private keys (pairs of files with the same name, - // except for ".cert" and ".key" suffixes). - DockerCertPath string - // DockerInsecureSkipTLSVerify turns off verification of TLS - // certificates and allows connecting to registries without encryption. - DockerInsecureSkipTLSVerify bool -} - -// SigningOptions encapsulates settings that control whether or not we strip or -// add signatures to images when writing them. -type signingOptions struct { - // RemoveSignatures directs us to remove any signatures which are already present. - RemoveSignatures bool - // SignBy is a key identifier of some kind, indicating that a signature should be generated using the specified private key and stored with the image. - SignBy string -} - func getStore(c *cli.Context) (storage.Store, error) { options := storage.DefaultStoreOptions if c.GlobalIsSet("root") { @@ -66,99 +37,6 @@ func getStore(c *cli.Context) (storage.Store, error) { return store, nil } -func getCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *dockerRegistryOptions, signing signingOptions) *cp.Options { - if srcDockerRegistry == nil { - srcDockerRegistry = &dockerRegistryOptions{} - } - if destDockerRegistry == nil { - destDockerRegistry = &dockerRegistryOptions{} - } - srcContext := srcDockerRegistry.getSystemContext(signaturePolicyPath) - destContext := destDockerRegistry.getSystemContext(signaturePolicyPath) - return &cp.Options{ - RemoveSignatures: signing.RemoveSignatures, - SignBy: signing.SignBy, - ReportWriter: reportWriter, - SourceCtx: srcContext, - DestinationCtx: destContext, - } -} - -func getSystemContext(signaturePolicyPath string) *types.SystemContext { - sc := &types.SystemContext{} - if signaturePolicyPath != "" { - sc.SignaturePolicyPath = signaturePolicyPath - } - return sc -} - -func copyStringStringMap(m map[string]string) map[string]string { - n := map[string]string{} - for k, v := range m { - n[k] = v - } - return n -} - -// A container FS is split into two parts. The first is the top layer, a -// mutable layer, and the rest is the RootFS: the set of immutable layers -// that make up the image on which the container is based -func getRootFsSize(store storage.Store, containerID string) (int64, error) { - ctrStore, err := store.ContainerStore() - if err != nil { - return 0, err - } - container, err := ctrStore.Get(containerID) - if err != nil { - return 0, err - } - lstore, err := store.LayerStore() - if err != nil { - return 0, err - } - - // Ignore the size of the top layer. The top layer is a mutable RW layer - // and is not considered a part of the rootfs - rwLayer, err := lstore.Get(container.LayerID) - if err != nil { - return 0, err - } - layer, err := lstore.Get(rwLayer.Parent) - if err != nil { - return 0, err - } - - size := int64(0) - for layer.Parent != "" { - layerSize, err := lstore.DiffSize(layer.Parent, layer.ID) - if err != nil { - return size, errors.Wrapf(err, "getting diffsize of layer %q and its parent %q", layer.ID, layer.Parent) - } - size += layerSize - layer, err = lstore.Get(layer.Parent) - if err != nil { - return 0, err - } - } - // Get the size of the last layer. Has to be outside of the loop - // because the parent of the last layer is "", andlstore.Get("") - // will return an error - layerSize, err := lstore.DiffSize(layer.Parent, layer.ID) - return size + layerSize, err -} - -func isTrue(str string) bool { - return str == "true" -} - -func isFalse(str string) bool { - return str == "false" -} - -func isValidBool(str string) bool { - return isTrue(str) || isFalse(str) -} - func getPolicyContext(path string) (*signature.PolicyContext, error) { policy, err := signature.DefaultPolicy(&types.SystemContext{SignaturePolicyPath: path}) if err != nil { @@ -180,13 +58,3 @@ func parseRegistryCreds(creds string) (*types.DockerAuthConfig, error) { } return cfg, nil } - -func (o dockerRegistryOptions) getSystemContext(signaturePolicyPath string) *types.SystemContext { - sc := &types.SystemContext{ - SignaturePolicyPath: signaturePolicyPath, - DockerAuthConfig: o.DockerRegistryCreds, - DockerCertPath: o.DockerCertPath, - DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify, - } - return sc -} diff --git a/cmd/kpod/history.go b/cmd/kpod/history.go index aaa6df7d..ae0a5d3f 100644 --- a/cmd/kpod/history.go +++ b/cmd/kpod/history.go @@ -15,6 +15,7 @@ import ( is "github.com/containers/image/storage" "github.com/containers/storage" units "github.com/docker/go-units" + "github.com/kubernetes-incubator/cri-o/libkpod/common" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -240,7 +241,7 @@ func createJSON(store storage.Store, opts historyOptions) ([]byte, error) { return nil, errors.Errorf("no such image %q: %v", opts.image, err) } - systemContext := getSystemContext("") + systemContext := common.GetSystemContext("") src, err := ref.NewImage(systemContext) if err != nil { diff --git a/cmd/kpod/images.go b/cmd/kpod/images.go index 678af48b..231182a0 100644 --- a/cmd/kpod/images.go +++ b/cmd/kpod/images.go @@ -8,6 +8,7 @@ import ( is "github.com/containers/image/storage" "github.com/containers/storage" + "github.com/kubernetes-incubator/cri-o/libkpod/common" "github.com/kubernetes-incubator/cri-o/libkpod/image" "github.com/pkg/errors" "github.com/urfave/cli" @@ -134,7 +135,7 @@ func parseFilter(images []storage.Image, filter string) (*filterParams, error) { pair := strings.SplitN(param, "=", 2) switch strings.TrimSpace(pair[0]) { case "dangling": - if isValidBool(pair[1]) { + if common.IsValidBool(pair[1]) { params.dangling = pair[1] } else { return nil, fmt.Errorf("invalid filter: '%s=[%s]'", pair[0], pair[1]) @@ -260,9 +261,9 @@ func matchesFilter(image storage.Image, store storage.Store, name string, params } func matchesDangling(name string, dangling string) bool { - if isFalse(dangling) && name != "" { + if common.IsFalse(dangling) && name != "" { return true - } else if isTrue(dangling) && name == "" { + } else if common.IsTrue(dangling) && name == "" { return true } return false diff --git a/cmd/kpod/inspect.go b/cmd/kpod/inspect.go index b6f61e39..63d5c231 100644 --- a/cmd/kpod/inspect.go +++ b/cmd/kpod/inspect.go @@ -6,6 +6,8 @@ import ( "os" "text/template" + "github.com/kubernetes-incubator/cri-o/libkpod" + libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -83,19 +85,19 @@ func inspectCmd(c *cli.Context) error { var data interface{} switch itemType { case inspectTypeContainer: - data, err = getContainerData(store, name, size) + data, err = libkpod.GetContainerData(store, name, size) if err != nil { return errors.Wrapf(err, "error parsing container data") } case inspectTypeImage: - data, err = getImageData(store, name) + data, err = libkpodimage.GetImageData(store, name) if err != nil { return errors.Wrapf(err, "error parsing image data") } case inspectAll: - ctrData, err := getContainerData(store, name, size) + ctrData, err := libkpod.GetContainerData(store, name, size) if err != nil { - imgData, err := getImageData(store, name) + imgData, err := libkpodimage.GetImageData(store, name) if err != nil { return errors.Wrapf(err, "error parsing image data") } diff --git a/cmd/kpod/pull.go b/cmd/kpod/pull.go index a39555d4..b04d53c2 100644 --- a/cmd/kpod/pull.go +++ b/cmd/kpod/pull.go @@ -12,6 +12,7 @@ import ( "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" "github.com/containers/storage" + "github.com/kubernetes-incubator/cri-o/libkpod/common" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -69,7 +70,7 @@ func pullCmd(c *cli.Context) error { allTags = c.Bool("all-tags") } - systemContext := getSystemContext("") + systemContext := common.GetSystemContext("") err = pullImage(store, image, allTags, systemContext) if err != nil { @@ -120,7 +121,7 @@ func pullImage(store storage.Store, imgName string, allTags bool, sc *types.Syst } defer policyContext.Destroy() - copyOptions := getCopyOptions(os.Stdout, "", nil, nil, signingOptions{}) + copyOptions := common.GetCopyOptions(os.Stdout, "", nil, nil, common.SigningOptions{}) fmt.Println(tag + ": pulling from " + fromName) return cp.Image(policyContext, destRef, srcRef, copyOptions) diff --git a/cmd/kpod/push.go b/cmd/kpod/push.go index d7f3f6b5..20e89361 100644 --- a/cmd/kpod/push.go +++ b/cmd/kpod/push.go @@ -12,6 +12,7 @@ import ( "github.com/containers/image/types" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" + "github.com/kubernetes-incubator/cri-o/libkpod/common" libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image" "github.com/pkg/errors" "github.com/urfave/cli" @@ -88,11 +89,11 @@ type pushOptions struct { // DockerRegistryOptions encapsulates settings that affect how we // connect or authenticate to a remote registry to which we want to // push the image. - dockerRegistryOptions + common.DockerRegistryOptions // SigningOptions encapsulates settings that control whether or not we // strip or add signatures to the image when pushing (uploading) the // image to a registry. - signingOptions + common.SigningOptions } func pushCmd(c *cli.Context) error { @@ -133,12 +134,12 @@ func pushCmd(c *cli.Context) error { Compression: compress, SignaturePolicyPath: signaturePolicy, Store: store, - dockerRegistryOptions: dockerRegistryOptions{ + DockerRegistryOptions: common.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, DockerCertPath: certPath, DockerInsecureSkipTLSVerify: skipVerify, }, - signingOptions: signingOptions{ + SigningOptions: common.SigningOptions{ RemoveSignatures: removeSignatures, SignBy: signBy, }, @@ -171,22 +172,22 @@ func pushImage(srcName, destName string, options pushOptions) error { if err != nil { return errors.Wrapf(err, "error locating image %q for importing settings", srcName) } - systemContext := getSystemContext(options.SignaturePolicyPath) - cid, err := importContainerImageDataFromImage(options.Store, systemContext, img.ID, "", "") + systemContext := common.GetSystemContext(options.SignaturePolicyPath) + cd, err := libkpodimage.ImportCopyDataFromImage(options.Store, systemContext, img.ID, "", "") if err != nil { return err } // Give the image we're producing the same ancestors as its source image - cid.FromImage = cid.Docker.ContainerConfig.Image - cid.FromImageID = string(cid.Docker.Parent) + cd.FromImage = cd.Docker.ContainerConfig.Image + cd.FromImageID = string(cd.Docker.Parent) // Prep the layers and manifest for export - src, err := cid.makeImageRef(manifest.GuessMIMEType(cid.Manifest), options.Compression, img.Names, img.TopLayer, nil) + src, err := cd.MakeImageRef(manifest.GuessMIMEType(cd.Manifest), options.Compression, img.Names, img.TopLayer, nil) if err != nil { return errors.Wrapf(err, "error copying layers and metadata") } - copyOptions := getCopyOptions(options.ReportWriter, options.SignaturePolicyPath, nil, &options.dockerRegistryOptions, options.signingOptions) + copyOptions := common.GetCopyOptions(options.ReportWriter, options.SignaturePolicyPath, nil, &options.DockerRegistryOptions, options.SigningOptions) // Copy the image to the remote destination err = cp.Image(policyContext, dest, src, copyOptions) diff --git a/libkpod/common/common.go b/libkpod/common/common.go new file mode 100644 index 00000000..7bd6cac8 --- /dev/null +++ b/libkpod/common/common.go @@ -0,0 +1,60 @@ +package common + +import ( + "io" + + cp "github.com/containers/image/copy" + "github.com/containers/image/types" +) + +// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters +func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions) *cp.Options { + if srcDockerRegistry == nil { + srcDockerRegistry = &DockerRegistryOptions{} + } + if destDockerRegistry == nil { + destDockerRegistry = &DockerRegistryOptions{} + } + srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath) + destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath) + return &cp.Options{ + RemoveSignatures: signing.RemoveSignatures, + SignBy: signing.SignBy, + ReportWriter: reportWriter, + SourceCtx: srcContext, + DestinationCtx: destContext, + } +} + +// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path +func GetSystemContext(signaturePolicyPath string) *types.SystemContext { + sc := &types.SystemContext{} + if signaturePolicyPath != "" { + sc.SignaturePolicyPath = signaturePolicyPath + } + return sc +} + +// CopyStringStringMap deep copies a map[string]string and returns the result +func CopyStringStringMap(m map[string]string) map[string]string { + n := map[string]string{} + for k, v := range m { + n[k] = v + } + return n +} + +// IsTrue determines whether the given string equals "true" +func IsTrue(str string) bool { + return str == "true" +} + +// IsFalse determines whether the given string equals "false" +func IsFalse(str string) bool { + return str == "false" +} + +// IsValidBool determines whether the given string equals "true" or "false" +func IsValidBool(str string) bool { + return IsTrue(str) || IsFalse(str) +} diff --git a/libkpod/common/dockerRegistryOptions.go b/libkpod/common/dockerRegistryOptions.go new file mode 100644 index 00000000..fdbaa059 --- /dev/null +++ b/libkpod/common/dockerRegistryOptions.go @@ -0,0 +1,33 @@ +package common + +import "github.com/containers/image/types" + +// DockerRegistryOptions encapsulates settings that affect how we connect or +// authenticate to a remote registry. +type DockerRegistryOptions struct { + // DockerRegistryCreds is the user name and password to supply in case + // we need to pull an image from a registry, and it requires us to + // authenticate. + DockerRegistryCreds *types.DockerAuthConfig + // DockerCertPath is the location of a directory containing CA + // certificates which will be used to verify the registry's certificate + // (all files with names ending in ".crt"), and possibly client + // certificates and private keys (pairs of files with the same name, + // except for ".cert" and ".key" suffixes). + DockerCertPath string + // DockerInsecureSkipTLSVerify turns off verification of TLS + // certificates and allows connecting to registries without encryption. + DockerInsecureSkipTLSVerify bool +} + +// GetSystemContext constructs a new system context from the given signaturePolicy path and the +// values in the DockerRegistryOptions +func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath string) *types.SystemContext { + sc := &types.SystemContext{ + SignaturePolicyPath: signaturePolicyPath, + DockerAuthConfig: o.DockerRegistryCreds, + DockerCertPath: o.DockerCertPath, + DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify, + } + return sc +} diff --git a/libkpod/common/signingOptions.go b/libkpod/common/signingOptions.go new file mode 100644 index 00000000..b7e14be8 --- /dev/null +++ b/libkpod/common/signingOptions.go @@ -0,0 +1,10 @@ +package common + +// SigningOptions encapsulates settings that control whether or not we strip or +// add signatures to images when writing them. +type SigningOptions struct { + // RemoveSignatures directs us to remove any signatures which are already present. + RemoveSignatures bool + // SignBy is a key identifier of some kind, indicating that a signature should be generated using the specified private key and stored with the image. + SignBy string +} diff --git a/libkpod/container.go b/libkpod/container.go index bafd6737..9873271c 100644 --- a/libkpod/container.go +++ b/libkpod/container.go @@ -2,6 +2,7 @@ package libkpod import ( cstorage "github.com/containers/storage" + "github.com/pkg/errors" ) // FindContainer searches for a container with the given name or ID in the given store @@ -46,3 +47,51 @@ func GetContainerRwSize(store cstorage.Store, containerID string) (int64, error) } return lstore.DiffSize(layer.Parent, layer.ID) } + +// GetContainerRootFsSize gets the size of the container's root filesystem +// A container FS is split into two parts. The first is the top layer, a +// mutable layer, and the rest is the RootFS: the set of immutable layers +// that make up the image on which the container is based +func GetContainerRootFsSize(store cstorage.Store, containerID string) (int64, error) { + ctrStore, err := store.ContainerStore() + if err != nil { + return 0, err + } + container, err := ctrStore.Get(containerID) + if err != nil { + return 0, err + } + lstore, err := store.LayerStore() + if err != nil { + return 0, err + } + + // Ignore the size of the top layer. The top layer is a mutable RW layer + // and is not considered a part of the rootfs + rwLayer, err := lstore.Get(container.LayerID) + if err != nil { + return 0, err + } + layer, err := lstore.Get(rwLayer.Parent) + if err != nil { + return 0, err + } + + size := int64(0) + for layer.Parent != "" { + layerSize, err := lstore.DiffSize(layer.Parent, layer.ID) + if err != nil { + return size, errors.Wrapf(err, "getting diffsize of layer %q and its parent %q", layer.ID, layer.Parent) + } + size += layerSize + layer, err = lstore.Get(layer.Parent) + if err != nil { + return 0, err + } + } + // Get the size of the last layer. Has to be outside of the loop + // because the parent of the last layer is "", andlstore.Get("") + // will return an error + layerSize, err := lstore.DiffSize(layer.Parent, layer.ID) + return size + layerSize, err +} diff --git a/cmd/kpod/containerData.go b/libkpod/containerData.go similarity index 82% rename from cmd/kpod/containerData.go rename to libkpod/containerData.go index 4f68dac1..8be75902 100644 --- a/cmd/kpod/containerData.go +++ b/libkpod/containerData.go @@ -1,4 +1,4 @@ -package main +package libkpod import ( "encoding/json" @@ -9,17 +9,18 @@ import ( "github.com/containers/storage" "github.com/kubernetes-incubator/cri-o/cmd/kpod/docker" - "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/kubernetes-incubator/cri-o/libkpod/common" "github.com/kubernetes-incubator/cri-o/libkpod/driver" + libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image" "github.com/kubernetes-incubator/cri-o/oci" "github.com/kubernetes-incubator/cri-o/pkg/annotations" - "github.com/kubernetes-incubator/cri-o/server" "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) -type containerData struct { +// ContainerData handles the data used when inspecting a container +type ContainerData struct { ID string Name string LogPath string @@ -54,12 +55,14 @@ type driverData struct { Data map[string]string } -func getContainerData(store storage.Store, name string, size bool) (*containerData, error) { +// GetContainerData gets the ContainerData for a container with the given name in the given store. +// If size is set to true, it will also determine the size of the container +func GetContainerData(store storage.Store, name string, size bool) (*ContainerData, error) { ctr, err := inspectContainer(store, name) if err != nil { return nil, errors.Wrapf(err, "error reading build container %q", name) } - cid, err := openContainer(store, name) + cid, err := libkpodimage.GetContainerCopyData(store, name) if err != nil { return nil, errors.Wrapf(err, "error reading container image data") } @@ -77,7 +80,7 @@ func getContainerData(store storage.Store, name string, size bool) (*containerDa if err != nil { return nil, err } - topLayer, err := libkpod.GetContainerTopLayerID(store, ctr.ID()) + topLayer, err := GetContainerTopLayerID(store, ctr.ID()) if err != nil { return nil, err } @@ -85,7 +88,7 @@ func getContainerData(store storage.Store, name string, size bool) (*containerDa if err != nil { return nil, err } - data := &containerData{ + data := &ContainerData{ ID: ctr.ID(), Name: ctr.Name(), LogPath: ctr.LogPath(), @@ -117,13 +120,13 @@ func getContainerData(store storage.Store, name string, size bool) (*containerDa } if size { - sizeRootFs, err := getRootFsSize(store, data.ID) + sizeRootFs, err := GetContainerRootFsSize(store, data.ID) if err != nil { return nil, errors.Wrapf(err, "error reading size for container %q", name) } data.SizeRootFs = uint(sizeRootFs) - sizeRw, err := libkpod.GetContainerRwSize(store, data.ID) + sizeRw, err := GetContainerRwSize(store, data.ID) if err != nil { return nil, errors.Wrapf(err, "error reading RWSize for container %q", name) } @@ -154,7 +157,7 @@ func inspectContainer(store storage.Store, container string) (*oci.Container, er // get an oci.Container instance for a given container ID func getOCIContainer(store storage.Store, container string) (*oci.Container, error) { - ctr, err := libkpod.FindContainer(store, container) + ctr, err := FindContainer(store, container) if err != nil { return nil, err } @@ -181,9 +184,9 @@ func getOCIContainer(store storage.Store, container string) (*oci.Container, err return nil, err } - tty := isTrue(m.Annotations[annotations.TTY]) - stdin := isTrue(m.Annotations[annotations.Stdin]) - stdinOnce := isTrue(m.Annotations[annotations.StdinOnce]) + tty := common.IsTrue(m.Annotations[annotations.TTY]) + stdin := common.IsTrue(m.Annotations[annotations.Stdin]) + stdinOnce := common.IsTrue(m.Annotations[annotations.StdinOnce]) containerPath, err := store.ContainerRunDirectory(ctr.ID) if err != nil { @@ -216,6 +219,6 @@ func getOCIContainer(store storage.Store, container string) (*oci.Container, err } func getOCIRuntime(store storage.Store, container string) (*oci.Runtime, error) { - config := server.DefaultConfig() - return oci.New(config.Runtime, config.RuntimeUntrustedWorkload, config.DefaultWorkloadTrust, config.Conmon, config.ConmonEnv, config.CgroupManager) + // TODO: Move server default config out of server so that it can be used instead of this + return oci.New("/usr/bin/runc", "", "runtime", "/usr/local/libexec/crio/conmon", []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, "cgroupfs") } diff --git a/libkpod/driver/driver.go b/libkpod/driver/driver.go index d49008b6..4db55852 100644 --- a/libkpod/driver/driver.go +++ b/libkpod/driver/driver.go @@ -2,11 +2,13 @@ package driver import cstorage "github.com/containers/storage" -type DriverData struct { +// Data handles the data for a storage driver +type Data struct { Name string Data map[string]string } +// GetDriverName returns the name of the driver for the given store func GetDriverName(store cstorage.Store) (string, error) { driver, err := store.GraphDriver() if err != nil { @@ -15,6 +17,7 @@ func GetDriverName(store cstorage.Store) (string, error) { return driver.String(), nil } +// GetDriverMetadata returns the metadata regarding the driver for the layer in the given store func GetDriverMetadata(store cstorage.Store, layerID string) (map[string]string, error) { driver, err := store.GraphDriver() if err != nil { diff --git a/cmd/kpod/containerImageRef.go b/libkpod/image/CopyRef.go similarity index 79% rename from cmd/kpod/containerImageRef.go rename to libkpod/image/CopyRef.go index f43a76ad..1c7db726 100644 --- a/cmd/kpod/containerImageRef.go +++ b/libkpod/image/CopyRef.go @@ -1,4 +1,4 @@ -package main +package image import ( "bytes" @@ -24,7 +24,8 @@ import ( "github.com/pkg/errors" ) -type containerImageRef struct { +// CopyRef handles image references used for copying images to/from remotes +type CopyRef struct { store storage.Store compression archive.Compression name reference.Named @@ -40,9 +41,9 @@ type containerImageRef struct { exporting bool } -type containerImageSource struct { +type copySource struct { path string - ref *containerImageRef + ref *CopyRef store storage.Store layerID string names []string @@ -55,8 +56,9 @@ type containerImageSource struct { exporting bool } -func (i *containerImageRef) NewImage(sc *types.SystemContext) (types.Image, error) { - src, err := i.NewImageSource(sc, nil) +// NewImage creates a new image from the given system context +func (c *CopyRef) NewImage(sc *types.SystemContext) (types.Image, error) { + src, err := c.NewImageSource(sc, nil) if err != nil { return nil, err } @@ -78,10 +80,11 @@ func selectManifestType(preferred string, acceptable, supported []string) string return selected } -func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestTypes []string) (src types.ImageSource, err error) { +// NewImageSource creates a new image source from the given system context and manifest +func (c *CopyRef) NewImageSource(sc *types.SystemContext, manifestTypes []string) (src types.ImageSource, err error) { // Decide which type of manifest and configuration output we're going to provide. supportedManifestTypes := []string{v1.MediaTypeImageManifest, docker.V2S2MediaTypeManifest} - manifestType := selectManifestType(i.preferredManifestType, manifestTypes, supportedManifestTypes) + manifestType := selectManifestType(c.preferredManifestType, manifestTypes, supportedManifestTypes) // If it's not a format we support, return an error. if manifestType != v1.MediaTypeImageManifest && manifestType != docker.V2S2MediaTypeManifest { return nil, errors.Errorf("no supported manifest types (attempted to use %q, only know %q and %q)", @@ -89,8 +92,8 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType } // Start building the list of layers using the read-write layer. layers := []string{} - layerID := i.layerID - layer, err := i.store.Layer(layerID) + layerID := c.layerID + layer, err := c.store.Layer(layerID) if err != nil { return nil, errors.Wrapf(err, "unable to read layer %q", layerID) } @@ -102,7 +105,7 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType err = nil break } - layer, err = i.store.Layer(layerID) + layer, err = c.store.Layer(layerID) if err != nil { return nil, errors.Wrapf(err, "unable to read layer %q", layerID) } @@ -127,12 +130,12 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType // Build fresh copies of the configurations so that we don't mess with the values in the Builder // object itself. oimage := v1.Image{} - err = json.Unmarshal(i.oconfig, &oimage) + err = json.Unmarshal(c.oconfig, &oimage) if err != nil { return nil, err } dimage := docker.V2Image{} - err = json.Unmarshal(i.dconfig, &dimage) + err = json.Unmarshal(c.dconfig, &dimage) if err != nil { return nil, err } @@ -146,7 +149,7 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType MediaType: v1.MediaTypeImageConfig, }, Layers: []v1.Descriptor{}, - Annotations: i.annotations, + Annotations: c.annotations, } dmanifest := docker.V2S2Manifest{ V2Versioned: docker.V2Versioned{ @@ -170,8 +173,8 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType omediaType := v1.MediaTypeImageLayer dmediaType := docker.V2S2MediaTypeUncompressedLayer // Figure out which media type we want to call this. Assume no compression. - if i.compression != archive.Uncompressed { - switch i.compression { + if c.compression != archive.Uncompressed { + switch c.compression { case archive.Gzip: omediaType = v1.MediaTypeImageLayerGzip dmediaType = docker.V2S2MediaTypeLayer @@ -185,7 +188,7 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType } } // If we're not re-exporting the data, just fake up layer and diff IDs for the manifest. - if !i.exporting { + if !c.exporting { fakeLayerDigest := digest.NewDigestFromHex(digest.Canonical.String(), layerID) // Add a note in the manifest about the layer. The blobs should be identified by their // possibly-compressed blob digests, but just use the layer IDs here. @@ -233,7 +236,7 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType counter := ioutils.NewWriteCounter(layerFile) multiWriter := io.MultiWriter(counter, destHasher.Hash()) // Compress the layer, if we're compressing it. - writer, err := archive.CompressStream(multiWriter, i.compression) + writer, err := archive.CompressStream(multiWriter, c.compression) if err != nil { return nil, errors.Wrapf(err, "error compressing layer %q", layerID) } @@ -243,7 +246,7 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType } writer.Close() layerFile.Close() - if i.compression == archive.Uncompressed { + if c.compression == archive.Uncompressed { if size != counter.Count { return nil, errors.Errorf("error storing layer %q to file: inconsistent layer size (copied %d, wrote %d)", layerID, size, counter.Count) } @@ -275,18 +278,18 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType dimage.RootFS.DiffIDs = append(dimage.RootFS.DiffIDs, srcHasher.Digest()) } - if i.addHistory { + if c.addHistory { // Build history notes in the image configurations. onews := v1.History{ - Created: &i.created, - CreatedBy: i.createdBy, + Created: &c.created, + CreatedBy: c.createdBy, Author: oimage.Author, EmptyLayer: false, } oimage.History = append(oimage.History, onews) dnews := docker.V2S2History{ - Created: i.created, - CreatedBy: i.createdBy, + Created: c.created, + CreatedBy: c.createdBy, Author: dimage.Author, EmptyLayer: false, } @@ -344,90 +347,97 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType default: panic("unreachable code: unsupported manifest type") } - src = &containerImageSource{ + src = ©Source{ path: path, - ref: i, - store: i.store, - layerID: i.layerID, - names: i.names, - addHistory: i.addHistory, - compression: i.compression, + ref: c, + store: c.store, + layerID: c.layerID, + names: c.names, + addHistory: c.addHistory, + compression: c.compression, config: config, configDigest: digest.Canonical.FromBytes(config), manifest: manifest, manifestType: manifestType, - exporting: i.exporting, + exporting: c.exporting, } return src, nil } -func (i *containerImageRef) NewImageDestination(sc *types.SystemContext) (types.ImageDestination, error) { +// NewImageDestination creates a new image destination from the given system context +func (c *CopyRef) NewImageDestination(sc *types.SystemContext) (types.ImageDestination, error) { return nil, errors.Errorf("can't write to a container") } -func (i *containerImageRef) DockerReference() reference.Named { - return i.name +// DockerReference gets the docker reference for the given CopyRef +func (c *CopyRef) DockerReference() reference.Named { + return c.name } -func (i *containerImageRef) StringWithinTransport() string { - if len(i.names) > 0 { - return i.names[0] +// StringWithinTransport returns the first name of the copyRef +func (c *CopyRef) StringWithinTransport() string { + if len(c.names) > 0 { + return c.names[0] } return "" } -func (i *containerImageRef) DeleteImage(*types.SystemContext) error { +// DeleteImage deletes an image in the CopyRef +func (c *CopyRef) DeleteImage(*types.SystemContext) error { // we were never here return nil } -func (i *containerImageRef) PolicyConfigurationIdentity() string { +// PolicyConfigurationIdentity returns the policy configuration for the CopyRef +func (c *CopyRef) PolicyConfigurationIdentity() string { return "" } -func (i *containerImageRef) PolicyConfigurationNamespaces() []string { +// PolicyConfigurationNamespaces returns the policy configuration namespace for the CopyRef +func (c *CopyRef) PolicyConfigurationNamespaces() []string { return nil } -func (i *containerImageRef) Transport() types.ImageTransport { +// Transport returns an ImageTransport for the given CopyRef +func (c *CopyRef) Transport() types.ImageTransport { return is.Transport } -func (i *containerImageSource) Close() error { - err := os.RemoveAll(i.path) +func (cs *copySource) Close() error { + err := os.RemoveAll(cs.path) if err != nil { - logrus.Errorf("error removing %q: %v", i.path, err) + logrus.Errorf("error removing %q: %v", cs.path, err) } return err } -func (i *containerImageSource) Reference() types.ImageReference { - return i.ref +func (cs *copySource) Reference() types.ImageReference { + return cs.ref } -func (i *containerImageSource) GetSignatures() ([][]byte, error) { +func (cs *copySource) GetSignatures() ([][]byte, error) { return nil, nil } -func (i *containerImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { +func (cs *copySource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { return []byte{}, "", errors.Errorf("TODO") } -func (i *containerImageSource) GetManifest() ([]byte, string, error) { - return i.manifest, i.manifestType, nil +func (cs *copySource) GetManifest() ([]byte, string, error) { + return cs.manifest, cs.manifestType, nil } -func (i *containerImageSource) GetBlob(blob types.BlobInfo) (reader io.ReadCloser, size int64, err error) { - if blob.Digest == i.configDigest { +func (cs *copySource) GetBlob(blob types.BlobInfo) (reader io.ReadCloser, size int64, err error) { + if blob.Digest == cs.configDigest { logrus.Debugf("start reading config") - reader := bytes.NewReader(i.config) + reader := bytes.NewReader(cs.config) closer := func() error { logrus.Debugf("finished reading config") return nil } return ioutils.NewReadCloserWrapper(reader, closer), reader.Size(), nil } - layerFile, err := os.OpenFile(filepath.Join(i.path, blob.Digest.String()), os.O_RDONLY, 0600) + layerFile, err := os.OpenFile(filepath.Join(cs.path, blob.Digest.String()), os.O_RDONLY, 0600) if err != nil { logrus.Debugf("error reading layer %q: %v", blob.Digest.String(), err) return nil, -1, err diff --git a/cmd/kpod/containerImageData.go b/libkpod/image/copyData.go similarity index 87% rename from cmd/kpod/containerImageData.go rename to libkpod/image/copyData.go index aed9d5dc..2dedbe56 100644 --- a/cmd/kpod/containerImageData.go +++ b/libkpod/image/copyData.go @@ -1,4 +1,4 @@ -package main +package image import ( "encoding/json" @@ -16,7 +16,7 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/docker/docker/pkg/ioutils" "github.com/kubernetes-incubator/cri-o/cmd/kpod/docker" - libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image" + "github.com/kubernetes-incubator/cri-o/libkpod/common" digest "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -34,7 +34,8 @@ const ( OCIv1ImageManifest = v1.MediaTypeImageManifest ) -type containerImageData struct { +// CopyData stores the basic data used when copying a container or image +type CopyData struct { store storage.Store // Type is used to help identify a build container's metadata. It @@ -71,7 +72,7 @@ type containerImageData struct { Docker docker.V2Image `json:"docker,omitempty"` } -func (c *containerImageData) initConfig() { +func (c *CopyData) initConfig() { image := ociv1.Image{} dimage := docker.V2Image{} if len(c.Config) > 0 { @@ -115,7 +116,7 @@ func (c *containerImageData) initConfig() { c.fixupConfig() } -func (c *containerImageData) fixupConfig() { +func (c *CopyData) fixupConfig() { if c.Docker.Config != nil { // Prefer image-level settings over those from the container it was built from c.Docker.ContainerConfig = *c.Docker.Config @@ -142,39 +143,39 @@ func (c *containerImageData) fixupConfig() { // OS returns a name of the OS on which a container built using this image //is intended to be run. -func (c *containerImageData) OS() string { +func (c *CopyData) OS() string { return c.OCIv1.OS } // SetOS sets the name of the OS on which a container built using this image // is intended to be run. -func (c *containerImageData) SetOS(os string) { +func (c *CopyData) SetOS(os string) { c.OCIv1.OS = os c.Docker.OS = os } // Architecture returns a name of the architecture on which a container built // using this image is intended to be run. -func (c *containerImageData) Architecture() string { +func (c *CopyData) Architecture() string { return c.OCIv1.Architecture } // SetArchitecture sets the name of the architecture on which ta container built // using this image is intended to be run. -func (c *containerImageData) SetArchitecture(arch string) { +func (c *CopyData) SetArchitecture(arch string) { c.OCIv1.Architecture = arch c.Docker.Architecture = arch } // WorkDir returns the default working directory for running commands in a container // built using this image. -func (c *containerImageData) WorkDir() string { +func (c *CopyData) WorkDir() string { return c.OCIv1.Config.WorkingDir } // SetWorkDir sets the location of the default working directory for running commands // in a container built using this image. -func (c *containerImageData) SetWorkDir(there string) { +func (c *CopyData) SetWorkDir(there string) { c.OCIv1.Config.WorkingDir = there c.Docker.Config.WorkingDir = there } @@ -339,11 +340,13 @@ func makeDockerV2S1Image(manifest docker.V2S1Manifest) (docker.V2Image, error) { return dimage, nil } -func (c *containerImageData) Annotations() map[string]string { - return copyStringStringMap(c.ImageAnnotations) +// Annotations gets the anotations of the container or image +func (c *CopyData) Annotations() map[string]string { + return common.CopyStringStringMap(c.ImageAnnotations) } -func (c *containerImageData) Save() error { +// Save the CopyData to disk +func (c *CopyData) Save() error { buildstate, err := json.Marshal(c) if err != nil { return err @@ -356,13 +359,14 @@ func (c *containerImageData) Save() error { } -func openContainer(store storage.Store, name string) (*containerImageData, error) { - var data *containerImageData +// GetContainerCopyData gets the copy data for a container +func GetContainerCopyData(store storage.Store, name string) (*CopyData, error) { + var data *CopyData var err error if name != "" { - data, err = openContainerImageData(store, name) + data, err = openCopyData(store, name) if os.IsNotExist(err) { - data, err = importContainerImageData(store, name, "") + data, err = importCopyData(store, name, "") } } if err != nil { @@ -375,17 +379,18 @@ func openContainer(store storage.Store, name string) (*containerImageData, error } -func openImage(store storage.Store, image string) (*containerImageData, error) { +// GetImageCopyData gets the copy data for an image +func GetImageCopyData(store storage.Store, image string) (*CopyData, error) { if image == "" { return nil, errors.Errorf("image name must be specified") } - img, err := libkpodimage.FindImage(store, image) + img, err := FindImage(store, image) if err != nil { return nil, errors.Wrapf(err, "error locating image %q for importing settings", image) } - systemContext := getSystemContext("") - data, err := importContainerImageDataFromImage(store, systemContext, img.ID, "", "") + systemContext := common.GetSystemContext("") + data, err := ImportCopyDataFromImage(store, systemContext, img.ID, "", "") if err != nil { return nil, errors.Wrapf(err, "error reading image") } @@ -396,7 +401,7 @@ func openImage(store storage.Store, image string) (*containerImageData, error) { } -func importContainerImageData(store storage.Store, container, signaturePolicyPath string) (*containerImageData, error) { +func importCopyData(store storage.Store, container, signaturePolicyPath string) (*CopyData, error) { if container == "" { return nil, errors.Errorf("container name must be specified") } @@ -406,9 +411,9 @@ func importContainerImageData(store storage.Store, container, signaturePolicyPat return nil, err } - systemContext := getSystemContext(signaturePolicyPath) + systemContext := common.GetSystemContext(signaturePolicyPath) - data, err := importContainerImageDataFromImage(store, systemContext, c.ImageID, container, c.ID) + data, err := ImportCopyDataFromImage(store, systemContext, c.ImageID, container, c.ID) if err != nil { return nil, err } @@ -426,13 +431,13 @@ func importContainerImageData(store storage.Store, container, signaturePolicyPat err = data.Save() if err != nil { - return nil, errors.Wrapf(err, "error saving containerImageData state") + return nil, errors.Wrapf(err, "error saving CopyData state") } return data, nil } -func openContainerImageData(store storage.Store, container string) (*containerImageData, error) { +func openCopyData(store storage.Store, container string) (*CopyData, error) { cdir, err := store.ContainerDirectory(container) if err != nil { return nil, err @@ -441,7 +446,7 @@ func openContainerImageData(store storage.Store, container string) (*containerIm if err != nil { return nil, err } - c := &containerImageData{} + c := &CopyData{} err = json.Unmarshal(buildstate, &c) if err != nil { return nil, err @@ -455,7 +460,8 @@ func openContainerImageData(store storage.Store, container string) (*containerIm } -func importContainerImageDataFromImage(store storage.Store, systemContext *types.SystemContext, imageID, containerName, containerID string) (*containerImageData, error) { +// ImportCopyDataFromImage creates copy data for an image with the given parameters +func ImportCopyDataFromImage(store storage.Store, systemContext *types.SystemContext, imageID, containerName, containerID string) (*CopyData, error) { manifest := []byte{} config := []byte{} imageName := "" @@ -485,7 +491,7 @@ func importContainerImageDataFromImage(store storage.Store, systemContext *types } } - data := &containerImageData{ + data := &CopyData{ store: store, Type: containerType, FromImage: imageName, @@ -504,7 +510,8 @@ func importContainerImageDataFromImage(store storage.Store, systemContext *types } -func (c *containerImageData) makeImageRef(manifestType string, compress archive.Compression, names []string, layerID string, historyTimestamp *time.Time) (types.ImageReference, error) { +// MakeImageRef converts a CopyData struct into a types.ImageReference +func (c *CopyData) MakeImageRef(manifestType string, compress archive.Compression, names []string, layerID string, historyTimestamp *time.Time) (types.ImageReference, error) { var name reference.Named if len(names) > 0 { if parsed, err := reference.ParseNamed(names[0]); err == nil { @@ -526,7 +533,7 @@ func (c *containerImageData) makeImageRef(manifestType string, compress archive. if historyTimestamp != nil { created = historyTimestamp.UTC() } - ref := &containerImageRef{ + ref := &CopyRef{ store: c.store, compression: compress, name: name, diff --git a/libkpod/image/image.go b/libkpod/image/image.go index f4cad714..c82fd4c7 100644 --- a/libkpod/image/image.go +++ b/libkpod/image/image.go @@ -44,7 +44,7 @@ func Size(store storage.Store, img storage.Image) (int64, error) { return imgSize, nil } -// TopLayer returns the ID of the top layer of the image +// GetTopLayerID returns the ID of the top layer of the image func GetTopLayerID(img storage.Image) (string, error) { metadata, err := ParseMetadata(img) if err != nil { diff --git a/cmd/kpod/imageData.go b/libkpod/image/imageData.go similarity index 86% rename from cmd/kpod/imageData.go rename to libkpod/image/imageData.go index 4c656fc8..e8c3d2ea 100644 --- a/cmd/kpod/imageData.go +++ b/libkpod/image/imageData.go @@ -1,4 +1,4 @@ -package main +package image import ( "encoding/json" @@ -6,13 +6,13 @@ import ( "github.com/containers/storage" "github.com/kubernetes-incubator/cri-o/libkpod/driver" - libkpodimage "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" ) -type imageData struct { +// ImageData handles the data used when inspecting a container +type ImageData struct { ID string Names []string Digests []digest.Digest @@ -27,7 +27,7 @@ type imageData struct { OS string Size uint VirtualSize uint - GraphDriver driverData + GraphDriver driver.Data RootFS ociv1.RootFS } @@ -57,13 +57,14 @@ type rootFS struct { Layers []string } -func getImageData(store storage.Store, name string) (*imageData, error) { - img, err := libkpodimage.FindImage(store, name) +// GetImageData gets the ImageData for a container with the given name in the given store. +func GetImageData(store storage.Store, name string) (*ImageData, error) { + img, err := FindImage(store, name) if err != nil { return nil, errors.Wrapf(err, "error reading image %q", name) } - cid, err := openImage(store, name) + cid, err := GetImageCopyData(store, name) if err != nil { return nil, errors.Wrapf(err, "error reading image %q", name) } @@ -101,7 +102,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) { return nil, err } - topLayerID, err := libkpodimage.GetTopLayerID(*img) + topLayerID, err := GetTopLayerID(*img) if err != nil { return nil, err } @@ -123,12 +124,12 @@ func getImageData(store storage.Store, name string) (*imageData, error) { return nil, err } - virtualSize, err := libkpodimage.Size(store, *img) + virtualSize, err := Size(store, *img) if err != nil { return nil, err } - return &imageData{ + return &ImageData{ ID: img.ID, Names: img.Names, Digests: digests, @@ -143,7 +144,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) { OS: cid.OCIv1.OS, Size: uint(size), VirtualSize: uint(virtualSize), - GraphDriver: driverData{ + GraphDriver: driver.Data{ Name: driverName, Data: driverMetadata, }, @@ -152,7 +153,7 @@ func getImageData(store storage.Store, name string) (*imageData, error) { } func getDigests(img storage.Image) ([]digest.Digest, error) { - metadata, err := libkpodimage.ParseMetadata(img) + metadata, err := ParseMetadata(img) if err != nil { return nil, err }