mirror of
https://github.com/mudler/luet.git
synced 2025-09-07 18:20:19 +00:00
Update vendor
This commit is contained in:
56
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md
generated
vendored
Normal file
56
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# `mutate`
|
||||
|
||||
[](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate)
|
||||
|
||||
The `v1.Image`, `v1.ImageIndex`, and `v1.Layer` interfaces provide only
|
||||
accessor methods, so they are essentially immutable. If you want to change
|
||||
something about them, you need to produce a new instance of that interface.
|
||||
|
||||
A common use case for this library is to read an image from somewhere (a source),
|
||||
change something about it, and write the image somewhere else (a sink).
|
||||
|
||||
Graphically, this looks something like:
|
||||
|
||||
<p align="center">
|
||||
<img src="/images/mutate.dot.svg" />
|
||||
</p>
|
||||
|
||||
## Mutations
|
||||
|
||||
This is obviously not a comprehensive set of useful transformations (PRs welcome!),
|
||||
but a rough summary of what the `mutate` package currently does:
|
||||
|
||||
### `Config` and `ConfigFile`
|
||||
|
||||
These allow you to change the [image configuration](https://github.com/opencontainers/image-spec/blob/master/config.md#properties),
|
||||
e.g. to change the entrypoint, environment, author, etc.
|
||||
|
||||
### `Time`, `Canonical`, and `CreatedAt`
|
||||
|
||||
These are useful in the context of [reproducible builds](https://reproducible-builds.org/),
|
||||
where you may want to strip timestamps and other non-reproducible information.
|
||||
|
||||
### `Append`, `AppendLayers`, and `AppendManifests`
|
||||
|
||||
These functions allow the extension of a `v1.Image` or `v1.ImageIndex` with
|
||||
new layers or manifests.
|
||||
|
||||
For constructing an image `FROM scratch`, see the [`empty`](/pkg/v1/empty) package.
|
||||
|
||||
### `MediaType` and `IndexMediaType`
|
||||
|
||||
Sometimes, it is necessary to change the media type of an image or index,
|
||||
e.g. to appease a registry with strict validation of images (_looking at you, GCR_).
|
||||
|
||||
### `Rebase`
|
||||
|
||||
Rebase has [its own README](/cmd/crane/rebase.md).
|
||||
|
||||
This is the underlying implementation of [`crane rebase`](https://github.com/google/go-containerregistry/blob/master/cmd/crane/doc/crane_rebase.md).
|
||||
|
||||
### `Extract`
|
||||
|
||||
Extract will flatten an image filesystem into a single tar stream,
|
||||
respecting whiteout files.
|
||||
|
||||
This is the underlying implementation of [`crane export`](https://github.com/google/go-containerregistry/blob/master/cmd/crane/doc/crane_export.md).
|
16
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go
generated
vendored
Normal file
16
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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 mutate provides facilities for mutating v1.Images of any kind.
|
||||
package mutate
|
270
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go
generated
vendored
Normal file
270
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright 2019 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 mutate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
"github.com/google/go-containerregistry/pkg/v1/stream"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
)
|
||||
|
||||
type image struct {
|
||||
base v1.Image
|
||||
adds []Addendum
|
||||
|
||||
computed bool
|
||||
configFile *v1.ConfigFile
|
||||
manifest *v1.Manifest
|
||||
mediaType *types.MediaType
|
||||
diffIDMap map[v1.Hash]v1.Layer
|
||||
digestMap map[v1.Hash]v1.Layer
|
||||
}
|
||||
|
||||
var _ v1.Image = (*image)(nil)
|
||||
|
||||
func (i *image) MediaType() (types.MediaType, error) {
|
||||
if i.mediaType != nil {
|
||||
return *i.mediaType, nil
|
||||
}
|
||||
return i.base.MediaType()
|
||||
}
|
||||
|
||||
func (i *image) compute() error {
|
||||
// Don't re-compute if already computed.
|
||||
if i.computed {
|
||||
return nil
|
||||
}
|
||||
var configFile *v1.ConfigFile
|
||||
if i.configFile != nil {
|
||||
configFile = i.configFile
|
||||
} else {
|
||||
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 {
|
||||
history = append(history, add.History)
|
||||
if add.Layer != nil {
|
||||
diffID, err := add.Layer.DiffID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
diffIDs = append(diffIDs, diffID)
|
||||
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 {
|
||||
if add.Layer == nil {
|
||||
// Empty layers include only history in manifest.
|
||||
continue
|
||||
}
|
||||
|
||||
desc, err := partial.Descriptor(add.Layer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fields in the addendum override the original descriptor.
|
||||
if len(add.Annotations) != 0 {
|
||||
desc.Annotations = add.Annotations
|
||||
}
|
||||
if len(add.URLs) != 0 {
|
||||
desc.URLs = add.URLs
|
||||
}
|
||||
|
||||
if add.MediaType != "" {
|
||||
desc.MediaType = add.MediaType
|
||||
}
|
||||
|
||||
manifestLayers = append(manifestLayers, *desc)
|
||||
digestMap[desc.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
|
||||
|
||||
// With OCI media types, this should not be set, see discussion:
|
||||
// https://github.com/opencontainers/image-spec/pull/795
|
||||
if i.mediaType != nil {
|
||||
if strings.Contains(string(*i.mediaType), types.OCIVendorPrefix) {
|
||||
manifest.MediaType = ""
|
||||
} else if strings.Contains(string(*i.mediaType), types.DockerVendorPrefix) {
|
||||
manifest.MediaType = *i.mediaType
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
ls := make([]v1.Layer, 0, len(diffIDs))
|
||||
for _, h := range diffIDs {
|
||||
l, err := i.LayerByDiffID(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ls = append(ls, l)
|
||||
}
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Size implements v1.Image.
|
||||
func (i *image) Size() (int64, error) {
|
||||
if err := i.compute(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return partial.Size(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)
|
||||
}
|
||||
|
||||
// LayerByDigest returns a Layer for interacting with a particular layer of
|
||||
// the image, looking it up by "digest" (the compressed hash).
|
||||
func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) {
|
||||
if cn, err := i.ConfigName(); err != nil {
|
||||
return nil, err
|
||||
} else if h == cn {
|
||||
return partial.ConfigLayer(i)
|
||||
}
|
||||
if layer, ok := i.digestMap[h]; ok {
|
||||
return layer, nil
|
||||
}
|
||||
return i.base.LayerByDigest(h)
|
||||
}
|
||||
|
||||
// LayerByDiffID is an analog to LayerByDigest, looking up by "diff id"
|
||||
// (the uncompressed hash).
|
||||
func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) {
|
||||
if layer, ok := i.diffIDMap[h]; ok {
|
||||
return layer, nil
|
||||
}
|
||||
return i.base.LayerByDiffID(h)
|
||||
}
|
||||
|
||||
func validate(adds []Addendum) error {
|
||||
for _, add := range adds {
|
||||
if add.Layer == nil && !add.History.EmptyLayer {
|
||||
return errors.New("unable to add a nil layer to the image")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
161
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go
generated
vendored
Normal file
161
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2019 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 mutate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/logs"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
)
|
||||
|
||||
func computeDescriptor(ia IndexAddendum) (*v1.Descriptor, error) {
|
||||
desc, err := partial.Descriptor(ia.Add)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The IndexAddendum allows overriding Descriptor values.
|
||||
if ia.Descriptor.Size != 0 {
|
||||
desc.Size = ia.Descriptor.Size
|
||||
}
|
||||
if string(ia.Descriptor.MediaType) != "" {
|
||||
desc.MediaType = ia.Descriptor.MediaType
|
||||
}
|
||||
if ia.Descriptor.Digest != (v1.Hash{}) {
|
||||
desc.Digest = ia.Descriptor.Digest
|
||||
}
|
||||
if ia.Descriptor.Platform != nil {
|
||||
desc.Platform = ia.Descriptor.Platform
|
||||
}
|
||||
if len(ia.Descriptor.URLs) != 0 {
|
||||
desc.URLs = ia.Descriptor.URLs
|
||||
}
|
||||
if len(ia.Descriptor.Annotations) != 0 {
|
||||
desc.Annotations = ia.Descriptor.Annotations
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
type index struct {
|
||||
base v1.ImageIndex
|
||||
adds []IndexAddendum
|
||||
|
||||
computed bool
|
||||
manifest *v1.IndexManifest
|
||||
mediaType *types.MediaType
|
||||
imageMap map[v1.Hash]v1.Image
|
||||
indexMap map[v1.Hash]v1.ImageIndex
|
||||
}
|
||||
|
||||
var _ v1.ImageIndex = (*index)(nil)
|
||||
|
||||
func (i *index) MediaType() (types.MediaType, error) {
|
||||
if i.mediaType != nil {
|
||||
return *i.mediaType, nil
|
||||
}
|
||||
return i.base.MediaType()
|
||||
}
|
||||
|
||||
func (i *index) Size() (int64, error) { return partial.Size(i) }
|
||||
|
||||
func (i *index) compute() error {
|
||||
// Don't re-compute if already computed.
|
||||
if i.computed {
|
||||
return nil
|
||||
}
|
||||
|
||||
i.imageMap = make(map[v1.Hash]v1.Image)
|
||||
i.indexMap = make(map[v1.Hash]v1.ImageIndex)
|
||||
|
||||
m, err := i.base.IndexManifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifest := m.DeepCopy()
|
||||
manifests := manifest.Manifests
|
||||
for _, add := range i.adds {
|
||||
desc, err := computeDescriptor(add)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifests = append(manifests, *desc)
|
||||
if idx, ok := add.Add.(v1.ImageIndex); ok {
|
||||
i.indexMap[desc.Digest] = idx
|
||||
} else if img, ok := add.Add.(v1.Image); ok {
|
||||
i.imageMap[desc.Digest] = img
|
||||
} else {
|
||||
logs.Warn.Printf("Unexpected index addendum: %T", add.Add)
|
||||
}
|
||||
}
|
||||
manifest.Manifests = manifests
|
||||
|
||||
// With OCI media types, this should not be set, see discussion:
|
||||
// https://github.com/opencontainers/image-spec/pull/795
|
||||
if i.mediaType != nil {
|
||||
if strings.Contains(string(*i.mediaType), types.OCIVendorPrefix) {
|
||||
manifest.MediaType = ""
|
||||
} else if strings.Contains(string(*i.mediaType), types.DockerVendorPrefix) {
|
||||
manifest.MediaType = *i.mediaType
|
||||
}
|
||||
}
|
||||
|
||||
i.manifest = manifest
|
||||
i.computed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *index) Image(h v1.Hash) (v1.Image, error) {
|
||||
if img, ok := i.imageMap[h]; ok {
|
||||
return img, nil
|
||||
}
|
||||
return i.base.Image(h)
|
||||
}
|
||||
|
||||
func (i *index) ImageIndex(h v1.Hash) (v1.ImageIndex, error) {
|
||||
if idx, ok := i.indexMap[h]; ok {
|
||||
return idx, nil
|
||||
}
|
||||
return i.base.ImageIndex(h)
|
||||
}
|
||||
|
||||
// Digest returns the sha256 of this image's manifest.
|
||||
func (i *index) 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 *index) IndexManifest() (*v1.IndexManifest, error) {
|
||||
if err := i.compute(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.manifest, nil
|
||||
}
|
||||
|
||||
// RawManifest returns the serialized bytes of Manifest()
|
||||
func (i *index) RawManifest() ([]byte, error) {
|
||||
if err := i.compute(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(i.manifest)
|
||||
}
|
387
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go
generated
vendored
Normal file
387
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go
generated
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
// 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 mutate
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/google/go-containerregistry/pkg/v1/v1util"
|
||||
)
|
||||
|
||||
const whiteoutPrefix = ".wh."
|
||||
|
||||
// Addendum contains layers and history to be appended
|
||||
// to a base image
|
||||
type Addendum struct {
|
||||
Layer v1.Layer
|
||||
History v1.History
|
||||
URLs []string
|
||||
Annotations map[string]string
|
||||
MediaType types.MediaType
|
||||
}
|
||||
|
||||
// AppendLayers applies layers to a base image.
|
||||
func AppendLayers(base v1.Image, layers ...v1.Layer) (v1.Image, error) {
|
||||
additions := make([]Addendum, 0, len(layers))
|
||||
for _, layer := range layers {
|
||||
additions = append(additions, Addendum{Layer: layer})
|
||||
}
|
||||
|
||||
return Append(base, additions...)
|
||||
}
|
||||
|
||||
// Append will apply the list of addendums to the base image
|
||||
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
|
||||
}
|
||||
|
||||
return &image{
|
||||
base: base,
|
||||
adds: adds,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Appendable is an interface that represents something that can be appended
|
||||
// to an ImageIndex. We need to be able to construct a v1.Descriptor in order
|
||||
// to append something, and this is the minimum required information for that.
|
||||
type Appendable interface {
|
||||
MediaType() (types.MediaType, error)
|
||||
Digest() (v1.Hash, error)
|
||||
Size() (int64, error)
|
||||
}
|
||||
|
||||
// IndexAddendum represents an appendable thing and all the properties that
|
||||
// we may want to override in the resulting v1.Descriptor.
|
||||
type IndexAddendum struct {
|
||||
Add Appendable
|
||||
v1.Descriptor
|
||||
}
|
||||
|
||||
// AppendManifests appends a manifest to the ImageIndex.
|
||||
func AppendManifests(base v1.ImageIndex, adds ...IndexAddendum) v1.ImageIndex {
|
||||
return &index{
|
||||
base: base,
|
||||
adds: adds,
|
||||
}
|
||||
}
|
||||
|
||||
// Config mutates the provided v1.Image to have the provided v1.Config
|
||||
func Config(base v1.Image, cfg v1.Config) (v1.Image, error) {
|
||||
cf, err := base.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cf.Config = cfg
|
||||
|
||||
return ConfigFile(base, cf)
|
||||
}
|
||||
|
||||
// ConfigFile mutates the provided v1.Image to have the provided v1.ConfigFile
|
||||
func ConfigFile(base v1.Image, cfg *v1.ConfigFile) (v1.Image, error) {
|
||||
m, err := base.Manifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
image := &image{
|
||||
base: base,
|
||||
manifest: m.DeepCopy(),
|
||||
configFile: cfg,
|
||||
}
|
||||
|
||||
return image, nil
|
||||
}
|
||||
|
||||
// CreatedAt mutates the provided v1.Image to have the provided v1.Time
|
||||
func CreatedAt(base v1.Image, created v1.Time) (v1.Image, error) {
|
||||
cf, err := base.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := cf.DeepCopy()
|
||||
cfg.Created = created
|
||||
|
||||
return ConfigFile(base, cfg)
|
||||
}
|
||||
|
||||
// Extract takes an image and returns an io.ReadCloser containing the image's
|
||||
// flattened filesystem.
|
||||
//
|
||||
// Callers can read the filesystem contents by passing the reader to
|
||||
// tar.NewReader, or io.Copy it directly to some output.
|
||||
//
|
||||
// If a caller doesn't read the full contents, they should Close it to free up
|
||||
// resources used during extraction.
|
||||
func Extract(img v1.Image) io.ReadCloser {
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
go func() {
|
||||
// Close the writer with any errors encountered during
|
||||
// extraction. These errors will be returned by the reader end
|
||||
// on subsequent reads. If err == nil, the reader will return
|
||||
// EOF.
|
||||
pw.CloseWithError(extract(img, pw))
|
||||
}()
|
||||
|
||||
return pr
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/google/containerregistry/blob/da03b395ccdc4e149e34fbb540483efce962dc64/client/v2_2/docker_image_.py#L816
|
||||
func extract(img v1.Image, w io.Writer) error {
|
||||
tarWriter := tar.NewWriter(w)
|
||||
defer tarWriter.Close()
|
||||
|
||||
fileMap := map[string]bool{}
|
||||
|
||||
layers, err := img.Layers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving image layers: %v", err)
|
||||
}
|
||||
// we iterate through the layers in reverse order because it makes handling
|
||||
// whiteout layers more efficient, since we can just keep track of the removed
|
||||
// files as we see .wh. layers and ignore those in previous layers.
|
||||
for i := len(layers) - 1; i >= 0; i-- {
|
||||
layer := layers[i]
|
||||
layerReader, err := layer.Uncompressed()
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading layer contents: %v", err)
|
||||
}
|
||||
defer layerReader.Close()
|
||||
tarReader := tar.NewReader(layerReader)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading tar: %v", err)
|
||||
}
|
||||
|
||||
basename := filepath.Base(header.Name)
|
||||
dirname := filepath.Dir(header.Name)
|
||||
tombstone := strings.HasPrefix(basename, whiteoutPrefix)
|
||||
if tombstone {
|
||||
basename = basename[len(whiteoutPrefix):]
|
||||
}
|
||||
|
||||
// check if we have seen value before
|
||||
// if we're checking a directory, don't filepath.Join names
|
||||
var name string
|
||||
if header.Typeflag == tar.TypeDir {
|
||||
name = header.Name
|
||||
} else {
|
||||
name = filepath.Join(dirname, basename)
|
||||
}
|
||||
|
||||
if _, ok := fileMap[name]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// check for a whited out parent directory
|
||||
if inWhiteoutDir(fileMap, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// mark file as handled. non-directory implicitly tombstones
|
||||
// any entries with a matching (or child) name
|
||||
fileMap[name] = tombstone || !(header.Typeflag == tar.TypeDir)
|
||||
if !tombstone {
|
||||
tarWriter.WriteHeader(header)
|
||||
if header.Size > 0 {
|
||||
if _, err := io.Copy(tarWriter, tarReader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func inWhiteoutDir(fileMap map[string]bool, file string) bool {
|
||||
for {
|
||||
if file == "" {
|
||||
break
|
||||
}
|
||||
dirname := filepath.Dir(file)
|
||||
if file == dirname {
|
||||
break
|
||||
}
|
||||
if val, ok := fileMap[dirname]; ok && val {
|
||||
return true
|
||||
}
|
||||
file = dirname
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Time sets all timestamps in an image to the given timestamp.
|
||||
func Time(img v1.Image, t time.Time) (v1.Image, error) {
|
||||
newImage := empty.Image
|
||||
|
||||
layers, err := img.Layers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting image layers: %v", err)
|
||||
}
|
||||
|
||||
// Strip away all timestamps from layers
|
||||
var newLayers []v1.Layer
|
||||
for _, layer := range layers {
|
||||
newLayer, err := layerTime(layer, t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting layer times: %v", err)
|
||||
}
|
||||
newLayers = append(newLayers, newLayer)
|
||||
}
|
||||
|
||||
newImage, err = AppendLayers(newImage, newLayers...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("appending layers: %v", err)
|
||||
}
|
||||
|
||||
ocf, err := img.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting original config file: %v", err)
|
||||
}
|
||||
|
||||
cf, err := newImage.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting config file: %v", err)
|
||||
}
|
||||
|
||||
cfg := cf.DeepCopy()
|
||||
|
||||
// Copy basic config over
|
||||
cfg.Architecture = ocf.Architecture
|
||||
cfg.OS = ocf.OS
|
||||
cfg.OSVersion = ocf.OSVersion
|
||||
cfg.Config = ocf.Config
|
||||
|
||||
// Strip away timestamps from the config file
|
||||
cfg.Created = v1.Time{Time: t}
|
||||
|
||||
for _, h := range cfg.History {
|
||||
h.Created = v1.Time{Time: t}
|
||||
}
|
||||
|
||||
return ConfigFile(newImage, cfg)
|
||||
}
|
||||
|
||||
func layerTime(layer v1.Layer, t time.Time) (v1.Layer, error) {
|
||||
layerReader, err := layer.Uncompressed()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting layer: %v", err)
|
||||
}
|
||||
defer layerReader.Close()
|
||||
w := new(bytes.Buffer)
|
||||
tarWriter := tar.NewWriter(w)
|
||||
defer tarWriter.Close()
|
||||
|
||||
tarReader := tar.NewReader(layerReader)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading layer: %v", err)
|
||||
}
|
||||
|
||||
header.ModTime = t
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return nil, fmt.Errorf("writing tar header: %v", err)
|
||||
}
|
||||
|
||||
if header.Typeflag == tar.TypeReg {
|
||||
if _, err = io.Copy(tarWriter, tarReader); err != nil {
|
||||
return nil, fmt.Errorf("writing layer file: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := tarWriter.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := w.Bytes()
|
||||
// gzip the contents, then create the layer
|
||||
opener := func() (io.ReadCloser, error) {
|
||||
return v1util.GzipReadCloser(ioutil.NopCloser(bytes.NewReader(b))), nil
|
||||
}
|
||||
layer, err = tarball.LayerFromOpener(opener)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating layer: %v", err)
|
||||
}
|
||||
|
||||
return layer, nil
|
||||
}
|
||||
|
||||
// Canonical is a helper function to combine Time and configFile
|
||||
// to remove any randomness during a docker build.
|
||||
func Canonical(img v1.Image) (v1.Image, error) {
|
||||
// Set all timestamps to 0
|
||||
created := time.Time{}
|
||||
img, err := Time(img, created)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cf, err := img.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get rid of host-dependent random config
|
||||
cfg := cf.DeepCopy()
|
||||
|
||||
cfg.Container = ""
|
||||
cfg.Config.Hostname = ""
|
||||
cfg.DockerVersion = ""
|
||||
|
||||
return ConfigFile(img, cfg)
|
||||
}
|
||||
|
||||
// MediaType modifies the MediaType() of the given image.
|
||||
func MediaType(img v1.Image, mt types.MediaType) v1.Image {
|
||||
return &image{
|
||||
base: img,
|
||||
mediaType: &mt,
|
||||
}
|
||||
}
|
||||
|
||||
// IndexMediaType modifies the MediaType() of the given index.
|
||||
func IndexMediaType(idx v1.ImageIndex, mt types.MediaType) v1.ImageIndex {
|
||||
return &index{
|
||||
base: idx,
|
||||
mediaType: &mt,
|
||||
}
|
||||
}
|
144
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go
generated
vendored
Normal file
144
vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// 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 mutate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
)
|
||||
|
||||
// Rebase returns a new v1.Image where the oldBase in orig is replaced by newBase.
|
||||
func Rebase(orig, oldBase, newBase v1.Image) (v1.Image, error) {
|
||||
// Verify that oldBase's layers are present in orig, otherwise orig is
|
||||
// not based on oldBase at all.
|
||||
origLayers, err := orig.Layers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layers for original: %v", err)
|
||||
}
|
||||
oldBaseLayers, err := oldBase.Layers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(oldBaseLayers) > len(origLayers) {
|
||||
return nil, fmt.Errorf("image %q is not based on %q (too few layers)", orig, oldBase)
|
||||
}
|
||||
for i, l := range oldBaseLayers {
|
||||
oldLayerDigest, err := l.Digest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get digest of layer %d of %q: %v", i, oldBase, err)
|
||||
}
|
||||
origLayerDigest, err := origLayers[i].Digest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get digest of layer %d of %q: %v", i, orig, err)
|
||||
}
|
||||
if oldLayerDigest != origLayerDigest {
|
||||
return nil, fmt.Errorf("image %q is not based on %q (layer %d mismatch)", orig, oldBase, i)
|
||||
}
|
||||
}
|
||||
|
||||
oldConfig, err := oldBase.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config for old base: %v", err)
|
||||
}
|
||||
|
||||
origConfig, err := orig.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config for original: %v", err)
|
||||
}
|
||||
|
||||
newConfig, err := newBase.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get config for new base: %v", err)
|
||||
}
|
||||
|
||||
// Stitch together an image that contains:
|
||||
// - original image's config
|
||||
// - new base image's os/arch properties
|
||||
// - new base image's layers + top of original image's layers
|
||||
// - new base image's history + top of original image's history
|
||||
rebasedImage, err := Config(empty.Image, *origConfig.Config.DeepCopy())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create empty image with original config: %v", err)
|
||||
}
|
||||
|
||||
// Add new config properties from existing images.
|
||||
rebasedConfig, err := rebasedImage.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get config for rebased image: %v", err)
|
||||
}
|
||||
// OS/Arch properties from new base
|
||||
rebasedConfig.Architecture = newConfig.Architecture
|
||||
rebasedConfig.OS = newConfig.OS
|
||||
rebasedConfig.OSVersion = newConfig.OSVersion
|
||||
|
||||
// Apply config properties to rebased.
|
||||
rebasedImage, err = ConfigFile(rebasedImage, rebasedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to replace config for rebased image: %v", err)
|
||||
}
|
||||
|
||||
// Get new base layers and config for history.
|
||||
newBaseLayers, err := newBase.Layers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get new base layers for new base: %v", err)
|
||||
}
|
||||
// Add new base layers.
|
||||
rebasedImage, err = Append(rebasedImage, createAddendums(0, 0, newConfig.History, newBaseLayers)...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to append new base image: %v", err)
|
||||
}
|
||||
|
||||
// Add original layers above the old base.
|
||||
rebasedImage, err = Append(rebasedImage, createAddendums(len(oldConfig.History), len(oldBaseLayers)+1, origConfig.History, origLayers)...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to append original image: %v", err)
|
||||
}
|
||||
|
||||
return rebasedImage, nil
|
||||
}
|
||||
|
||||
// createAddendums makes a list of addendums from a history and layers starting from a specific history and layer
|
||||
// indexes.
|
||||
func createAddendums(startHistory, startLayer int, history []v1.History, layers []v1.Layer) []Addendum {
|
||||
var adds []Addendum
|
||||
// History should be a superset of layers; empty layers (e.g. ENV statements) only exist in history.
|
||||
// They cannot be iterated identically but must be walked independently, only advancing the iterator for layers
|
||||
// when a history entry for a non-empty layer is seen.
|
||||
layerIndex := 0
|
||||
for historyIndex := range history {
|
||||
var layer v1.Layer
|
||||
emptyLayer := history[historyIndex].EmptyLayer
|
||||
if !emptyLayer {
|
||||
layer = layers[layerIndex]
|
||||
layerIndex++
|
||||
}
|
||||
if historyIndex >= startHistory || layerIndex >= startLayer {
|
||||
adds = append(adds, Addendum{
|
||||
Layer: layer,
|
||||
History: history[historyIndex],
|
||||
})
|
||||
}
|
||||
}
|
||||
// In the event history was malformed or non-existent, append the remaining layers.
|
||||
for i := layerIndex; i < len(layers); i++ {
|
||||
if i >= startLayer {
|
||||
adds = append(adds, Addendum{Layer: layers[layerIndex]})
|
||||
}
|
||||
}
|
||||
|
||||
return adds
|
||||
}
|
Reference in New Issue
Block a user