github.com/stevegt/moby@v1.13.1/image/v1/imagev1.go (about)

     1  package v1
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/distribution/digest"
    12  	"github.com/docker/docker/api/types/versions"
    13  	"github.com/docker/docker/image"
    14  	"github.com/docker/docker/layer"
    15  )
    16  
    17  var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
    18  
    19  // noFallbackMinVersion is the minimum version for which v1compatibility
    20  // information will not be marshaled through the Image struct to remove
    21  // blank fields.
    22  var noFallbackMinVersion = "1.8.3"
    23  
    24  // HistoryFromConfig creates a History struct from v1 configuration JSON
    25  func HistoryFromConfig(imageJSON []byte, emptyLayer bool) (image.History, error) {
    26  	h := image.History{}
    27  	var v1Image image.V1Image
    28  	if err := json.Unmarshal(imageJSON, &v1Image); err != nil {
    29  		return h, err
    30  	}
    31  
    32  	return image.History{
    33  		Author:     v1Image.Author,
    34  		Created:    v1Image.Created,
    35  		CreatedBy:  strings.Join(v1Image.ContainerConfig.Cmd, " "),
    36  		Comment:    v1Image.Comment,
    37  		EmptyLayer: emptyLayer,
    38  	}, nil
    39  }
    40  
    41  // CreateID creates an ID from v1 image, layerID and parent ID.
    42  // Used for backwards compatibility with old clients.
    43  func CreateID(v1Image image.V1Image, layerID layer.ChainID, parent digest.Digest) (digest.Digest, error) {
    44  	v1Image.ID = ""
    45  	v1JSON, err := json.Marshal(v1Image)
    46  	if err != nil {
    47  		return "", err
    48  	}
    49  
    50  	var config map[string]*json.RawMessage
    51  	if err := json.Unmarshal(v1JSON, &config); err != nil {
    52  		return "", err
    53  	}
    54  
    55  	// FIXME: note that this is slightly incompatible with RootFS logic
    56  	config["layer_id"] = rawJSON(layerID)
    57  	if parent != "" {
    58  		config["parent"] = rawJSON(parent)
    59  	}
    60  
    61  	configJSON, err := json.Marshal(config)
    62  	if err != nil {
    63  		return "", err
    64  	}
    65  	logrus.Debugf("CreateV1ID %s", configJSON)
    66  
    67  	return digest.FromBytes(configJSON), nil
    68  }
    69  
    70  // MakeConfigFromV1Config creates an image config from the legacy V1 config format.
    71  func MakeConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) ([]byte, error) {
    72  	var dver struct {
    73  		DockerVersion string `json:"docker_version"`
    74  	}
    75  
    76  	if err := json.Unmarshal(imageJSON, &dver); err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	useFallback := versions.LessThan(dver.DockerVersion, noFallbackMinVersion)
    81  
    82  	if useFallback {
    83  		var v1Image image.V1Image
    84  		err := json.Unmarshal(imageJSON, &v1Image)
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		imageJSON, err = json.Marshal(v1Image)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  	}
    93  
    94  	var c map[string]*json.RawMessage
    95  	if err := json.Unmarshal(imageJSON, &c); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	delete(c, "id")
   100  	delete(c, "parent")
   101  	delete(c, "Size") // Size is calculated from data on disk and is inconsistent
   102  	delete(c, "parent_id")
   103  	delete(c, "layer_id")
   104  	delete(c, "throwaway")
   105  
   106  	c["rootfs"] = rawJSON(rootfs)
   107  	c["history"] = rawJSON(history)
   108  
   109  	return json.Marshal(c)
   110  }
   111  
   112  // MakeV1ConfigFromConfig creates an legacy V1 image config from an Image struct
   113  func MakeV1ConfigFromConfig(img *image.Image, v1ID, parentV1ID string, throwaway bool) ([]byte, error) {
   114  	// Top-level v1compatibility string should be a modified version of the
   115  	// image config.
   116  	var configAsMap map[string]*json.RawMessage
   117  	if err := json.Unmarshal(img.RawJSON(), &configAsMap); err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	// Delete fields that didn't exist in old manifest
   122  	imageType := reflect.TypeOf(img).Elem()
   123  	for i := 0; i < imageType.NumField(); i++ {
   124  		f := imageType.Field(i)
   125  		jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
   126  		// Parent is handled specially below.
   127  		if jsonName != "" && jsonName != "parent" {
   128  			delete(configAsMap, jsonName)
   129  		}
   130  	}
   131  	configAsMap["id"] = rawJSON(v1ID)
   132  	if parentV1ID != "" {
   133  		configAsMap["parent"] = rawJSON(parentV1ID)
   134  	}
   135  	if throwaway {
   136  		configAsMap["throwaway"] = rawJSON(true)
   137  	}
   138  
   139  	return json.Marshal(configAsMap)
   140  }
   141  
   142  func rawJSON(value interface{}) *json.RawMessage {
   143  	jsonval, err := json.Marshal(value)
   144  	if err != nil {
   145  		return nil
   146  	}
   147  	return (*json.RawMessage)(&jsonval)
   148  }
   149  
   150  // ValidateID checks whether an ID string is a valid image ID.
   151  func ValidateID(id string) error {
   152  	if ok := validHex.MatchString(id); !ok {
   153  		return fmt.Errorf("image ID %q is invalid", id)
   154  	}
   155  	return nil
   156  }