...

Source file src/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go

Documentation: github.com/google/go-containerregistry/pkg/v1/mutate

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mutate
    16  
    17  import (
    18  	"fmt"
    19  
    20  	v1 "github.com/google/go-containerregistry/pkg/v1"
    21  	"github.com/google/go-containerregistry/pkg/v1/empty"
    22  )
    23  
    24  // Rebase returns a new v1.Image where the oldBase in orig is replaced by newBase.
    25  func Rebase(orig, oldBase, newBase v1.Image) (v1.Image, error) {
    26  	// Verify that oldBase's layers are present in orig, otherwise orig is
    27  	// not based on oldBase at all.
    28  	origLayers, err := orig.Layers()
    29  	if err != nil {
    30  		return nil, fmt.Errorf("failed to get layers for original: %w", err)
    31  	}
    32  	oldBaseLayers, err := oldBase.Layers()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	if len(oldBaseLayers) > len(origLayers) {
    37  		return nil, fmt.Errorf("image %q is not based on %q (too few layers)", orig, oldBase)
    38  	}
    39  	for i, l := range oldBaseLayers {
    40  		oldLayerDigest, err := l.Digest()
    41  		if err != nil {
    42  			return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, oldBase, err)
    43  		}
    44  		origLayerDigest, err := origLayers[i].Digest()
    45  		if err != nil {
    46  			return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, orig, err)
    47  		}
    48  		if oldLayerDigest != origLayerDigest {
    49  			return nil, fmt.Errorf("image %q is not based on %q (layer %d mismatch)", orig, oldBase, i)
    50  		}
    51  	}
    52  
    53  	oldConfig, err := oldBase.ConfigFile()
    54  	if err != nil {
    55  		return nil, fmt.Errorf("failed to get config for old base: %w", err)
    56  	}
    57  
    58  	origConfig, err := orig.ConfigFile()
    59  	if err != nil {
    60  		return nil, fmt.Errorf("failed to get config for original: %w", err)
    61  	}
    62  
    63  	newConfig, err := newBase.ConfigFile()
    64  	if err != nil {
    65  		return nil, fmt.Errorf("could not get config for new base: %w", err)
    66  	}
    67  
    68  	// Stitch together an image that contains:
    69  	// - original image's config
    70  	// - new base image's os/arch properties
    71  	// - new base image's layers + top of original image's layers
    72  	// - new base image's history + top of original image's history
    73  	rebasedImage, err := Config(empty.Image, *origConfig.Config.DeepCopy())
    74  	if err != nil {
    75  		return nil, fmt.Errorf("failed to create empty image with original config: %w", err)
    76  	}
    77  
    78  	// Add new config properties from existing images.
    79  	rebasedConfig, err := rebasedImage.ConfigFile()
    80  	if err != nil {
    81  		return nil, fmt.Errorf("could not get config for rebased image: %w", err)
    82  	}
    83  	// OS/Arch properties from new base
    84  	rebasedConfig.Architecture = newConfig.Architecture
    85  	rebasedConfig.OS = newConfig.OS
    86  	rebasedConfig.OSVersion = newConfig.OSVersion
    87  
    88  	// Apply config properties to rebased.
    89  	rebasedImage, err = ConfigFile(rebasedImage, rebasedConfig)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("failed to replace config for rebased image: %w", err)
    92  	}
    93  
    94  	// Get new base layers and config for history.
    95  	newBaseLayers, err := newBase.Layers()
    96  	if err != nil {
    97  		return nil, fmt.Errorf("could not get new base layers for new base: %w", err)
    98  	}
    99  	// Add new base layers.
   100  	rebasedImage, err = Append(rebasedImage, createAddendums(0, 0, newConfig.History, newBaseLayers)...)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("failed to append new base image: %w", err)
   103  	}
   104  
   105  	// Add original layers above the old base.
   106  	rebasedImage, err = Append(rebasedImage, createAddendums(len(oldConfig.History), len(oldBaseLayers)+1, origConfig.History, origLayers)...)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("failed to append original image: %w", err)
   109  	}
   110  
   111  	return rebasedImage, nil
   112  }
   113  
   114  // createAddendums makes a list of addendums from a history and layers starting from a specific history and layer
   115  // indexes.
   116  func createAddendums(startHistory, startLayer int, history []v1.History, layers []v1.Layer) []Addendum {
   117  	var adds []Addendum
   118  	// History should be a superset of layers; empty layers (e.g. ENV statements) only exist in history.
   119  	// They cannot be iterated identically but must be walked independently, only advancing the iterator for layers
   120  	// when a history entry for a non-empty layer is seen.
   121  	layerIndex := 0
   122  	for historyIndex := range history {
   123  		var layer v1.Layer
   124  		emptyLayer := history[historyIndex].EmptyLayer
   125  		if !emptyLayer {
   126  			layer = layers[layerIndex]
   127  			layerIndex++
   128  		}
   129  		if historyIndex >= startHistory || layerIndex >= startLayer {
   130  			adds = append(adds, Addendum{
   131  				Layer:   layer,
   132  				History: history[historyIndex],
   133  			})
   134  		}
   135  	}
   136  	// In the event history was malformed or non-existent, append the remaining layers.
   137  	for i := layerIndex; i < len(layers); i++ {
   138  		if i >= startLayer {
   139  			adds = append(adds, Addendum{Layer: layers[layerIndex]})
   140  		}
   141  	}
   142  
   143  	return adds
   144  }
   145  

View as plain text