kaniko/vendor/github.com/google/go-containerregistry/v1/tarball/write.go

123 lines
2.9 KiB
Go

// Copyright 2018 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tarball
import (
"archive/tar"
"bytes"
"encoding/json"
"io"
"os"
"github.com/google/go-containerregistry/name"
"github.com/google/go-containerregistry/v1"
)
// WriteOptions are used to expose optional information to guide or
// control the image write.
type WriteOptions struct {
// TODO(mattmoor): Whether to store things compressed?
}
// Write saves the image as the given tag in a tarball at the given path.
func Write(p string, tag name.Tag, img v1.Image, wo *WriteOptions) error {
// Write in the compressed format.
// This is a tarball, on-disk, with:
// One manifest.json file at the top level containing information about several images.
// One file for each layer, named after the layer's SHA.
// One file for the config blob, named after its SHA.
w, err := os.Create(p)
if err != nil {
return err
}
defer w.Close()
tf := tar.NewWriter(w)
defer tf.Close()
// Write the config.
cfgName, err := img.ConfigName()
if err != nil {
return err
}
cfg, err := img.ConfigFile()
if err != nil {
return err
}
cfgBlob, err := json.Marshal(cfg)
if err != nil {
return err
}
if err := writeFile(tf, cfgName.String(), bytes.NewReader(cfgBlob), int64(len(cfgBlob))); err != nil {
return err
}
// Write the layers.
layers, err := img.Layers()
if err != nil {
return err
}
digests := make([]string, len(layers))
for i, l := range layers {
d, err := l.Digest()
if err != nil {
return err
}
digests[i] = d.String()
r, err := l.Compressed()
if err != nil {
return err
}
blobSize, err := l.Size()
if err != nil {
return err
}
if err := writeFile(tf, d.String(), r, blobSize); err != nil {
return err
}
}
// Generate the tar descriptor and write it.
td := tarDescriptor{
singleImageTarDescriptor{
Config: cfgName.String(),
RepoTags: []string{tag.String()},
Layers: digests,
},
}
tdBytes, err := json.Marshal(td)
if err != nil {
return err
}
return writeFile(tf, "manifest.json", bytes.NewReader(tdBytes), int64(len(tdBytes)))
}
func writeFile(tf *tar.Writer, path string, r io.Reader, size int64) error {
hdr := &tar.Header{
Mode: 0644,
Typeflag: tar.TypeReg,
Size: size,
Name: path,
}
if err := tf.WriteHeader(hdr); err != nil {
return err
}
_, err := io.Copy(tf, r)
return err
}