Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pkg/v1/stream.NewLayer to stream layer contents #301

Merged
merged 10 commits into from
Nov 13, 2018
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ language:

go:
- "1.10"
- "1.11"

git:
depth: 1
Expand Down
3 changes: 1 addition & 2 deletions pkg/crane/append.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ import (
"log"
"net/http"

"github.com/spf13/cobra"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/spf13/cobra"
)

func init() { Root.AddCommand(NewCmdAppend()) }
Expand Down
203 changes: 129 additions & 74 deletions pkg/v1/mutate/mutate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/stream"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/google/go-containerregistry/pkg/v1/v1util"
Expand Down Expand Up @@ -58,77 +59,14 @@ func Append(base v1.Image, adds ...Addendum) (v1.Image, error) {
if len(adds) == 0 {
return base, nil
}

if err := validate(adds); err != nil {
return nil, err
}

m, err := base.Manifest()
if err != nil {
return nil, err
}

cf, err := base.ConfigFile()
if err != nil {
return nil, err
}

image := &image{
Image: base,
manifest: m.DeepCopy(),
configFile: cf.DeepCopy(),
diffIDMap: make(map[v1.Hash]v1.Layer),
digestMap: make(map[v1.Hash]v1.Layer),
}

diffIDs := image.configFile.RootFS.DiffIDs
history := image.configFile.History

for _, add := range adds {
diffID, err := add.Layer.DiffID()
if err != nil {
return nil, err
}
diffIDs = append(diffIDs, diffID)
history = append(history, add.History)
image.diffIDMap[diffID] = add.Layer
}

manifestLayers := image.manifest.Layers

for _, add := range adds {
d := v1.Descriptor{
MediaType: types.DockerLayer,
}

if d.Size, err = add.Layer.Size(); err != nil {
return nil, err
}

if d.Digest, err = add.Layer.Digest(); err != nil {
return nil, err
}

manifestLayers = append(manifestLayers, d)
image.digestMap[d.Digest] = add.Layer
}

image.configFile.RootFS.DiffIDs = diffIDs
image.configFile.History = history
image.manifest.Layers = manifestLayers

rcfg, err := image.RawConfigFile()
if err != nil {
return nil, err
}
d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg))
if err != nil {
return nil, err
}
image.manifest.Config.Digest = d
image.manifest.Config.Size = sz

return image, nil
return &image{
base: base,
adds: adds,
}, nil
}

// Config mutates the provided v1.Image to have the provided v1.Config
Expand All @@ -150,10 +88,8 @@ func configFile(base v1.Image, cfg *v1.ConfigFile) (v1.Image, error) {
}

image := &image{
Image: base,
manifest: m.DeepCopy(),
configFile: cfg,
digestMap: make(map[v1.Hash]v1.Layer),
base: base,
manifest: m.DeepCopy(),
}

rcfg, err := image.RawConfigFile()
Expand All @@ -166,6 +102,7 @@ func configFile(base v1.Image, cfg *v1.ConfigFile) (v1.Image, error) {
}
image.manifest.Config.Digest = d
image.manifest.Config.Size = sz
image.configFile = cfg
return image, nil
}

Expand All @@ -183,16 +120,113 @@ func CreatedAt(base v1.Image, created v1.Time) (v1.Image, error) {
}

type image struct {
v1.Image
base v1.Image
adds []Addendum

computed bool
configFile *v1.ConfigFile
manifest *v1.Manifest
diffIDMap map[v1.Hash]v1.Layer
digestMap map[v1.Hash]v1.Layer
}

var _ v1.Image = (*image)(nil)

func (i *image) MediaType() (types.MediaType, error) { return i.base.MediaType() }

func (i *image) compute() error {
// Don't re-compute if already computed.
if i.computed {
return nil
}
cf, err := i.base.ConfigFile()
if err != nil {
return err
}
configFile := cf.DeepCopy()
diffIDs := configFile.RootFS.DiffIDs
history := configFile.History

diffIDMap := make(map[v1.Hash]v1.Layer)
digestMap := make(map[v1.Hash]v1.Layer)

for _, add := range i.adds {
diffID, err := add.Layer.DiffID()
if err != nil {
return err
}
diffIDs = append(diffIDs, diffID)
history = append(history, add.History)
diffIDMap[diffID] = add.Layer
}

m, err := i.base.Manifest()
if err != nil {
return err
}
manifest := m.DeepCopy()
manifestLayers := manifest.Layers
for _, add := range i.adds {
d := v1.Descriptor{
MediaType: types.DockerLayer,
}

var err error
if d.Size, err = add.Layer.Size(); err != nil {
return err
}

if d.Digest, err = add.Layer.Digest(); err != nil {
return err
}

manifestLayers = append(manifestLayers, d)
digestMap[d.Digest] = add.Layer
}

configFile.RootFS.DiffIDs = diffIDs
configFile.History = history

manifest.Layers = manifestLayers

rcfg, err := json.Marshal(configFile)
if err != nil {
return err
}
d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg))
if err != nil {
return err
}
manifest.Config.Digest = d
manifest.Config.Size = sz

i.configFile = configFile
i.manifest = manifest
i.diffIDMap = diffIDMap
i.digestMap = digestMap
i.computed = true
return nil
}

// Layers returns the ordered collection of filesystem layers that comprise this image.
// The order of the list is oldest/base layer first, and most-recent/top layer last.
func (i *image) Layers() ([]v1.Layer, error) {
if err := i.compute(); err == stream.ErrNotComputed {
// Image contains a streamable layer which has not yet been
// consumed. Just return the layers we have in case the caller
// is going to consume the layers.
layers, err := i.base.Layers()
if err != nil {
return nil, err
}
for _, add := range i.adds {
layers = append(layers, add.Layer)
}
return layers, nil
} else if err != nil {
return nil, err
}

diffIDs, err := partial.DiffIDs(i)
if err != nil {
return nil, err
Expand All @@ -210,36 +244,57 @@ func (i *image) Layers() ([]v1.Layer, error) {

// BlobSet returns an unordered collection of all the blobs in the image.
func (i *image) BlobSet() (map[v1.Hash]struct{}, error) {
if err := i.compute(); err != nil {
return nil, err
}
return partial.BlobSet(i)
}

// ConfigName returns the hash of the image's config file.
func (i *image) ConfigName() (v1.Hash, error) {
if err := i.compute(); err != nil {
return v1.Hash{}, err
}
return partial.ConfigName(i)
}

// ConfigFile returns this image's config file.
func (i *image) ConfigFile() (*v1.ConfigFile, error) {
if err := i.compute(); err != nil {
return nil, err
}
return i.configFile, nil
}

// RawConfigFile returns the serialized bytes of ConfigFile()
func (i *image) RawConfigFile() ([]byte, error) {
if err := i.compute(); err != nil {
return nil, err
}
return json.Marshal(i.configFile)
}

// Digest returns the sha256 of this image's manifest.
func (i *image) Digest() (v1.Hash, error) {
if err := i.compute(); err != nil {
return v1.Hash{}, err
}
return partial.Digest(i)
}

// Manifest returns this image's Manifest object.
func (i *image) Manifest() (*v1.Manifest, error) {
if err := i.compute(); err != nil {
return nil, err
}
return i.manifest, nil
}

// RawManifest returns the serialized bytes of Manifest()
func (i *image) RawManifest() ([]byte, error) {
if err := i.compute(); err != nil {
return nil, err
}
return json.Marshal(i.manifest)
}

Expand All @@ -254,7 +309,7 @@ func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) {
if layer, ok := i.digestMap[h]; ok {
return layer, nil
}
return i.Image.LayerByDigest(h)
return i.base.LayerByDigest(h)
}

// LayerByDiffID is an analog to LayerByDigest, looking up by "diff id"
Expand All @@ -263,7 +318,7 @@ func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) {
if layer, ok := i.diffIDMap[h]; ok {
return layer, nil
}
return i.Image.LayerByDiffID(h)
return i.base.LayerByDiffID(h)
}

func validate(adds []Addendum) error {
Expand Down
Loading