2018-02-05 16:05:59 -05:00
|
|
|
package v1 // import "github.com/docker/docker/image/v1"
|
2015-11-18 17:18:07 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2016-03-16 21:45:40 -04:00
|
|
|
"reflect"
|
2015-11-18 17:18:07 -05:00
|
|
|
"strings"
|
|
|
|
|
2016-09-06 14:18:12 -04:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
2015-11-18 17:18:07 -05:00
|
|
|
"github.com/docker/docker/image"
|
|
|
|
"github.com/docker/docker/layer"
|
2016-12-22 16:26:30 -05:00
|
|
|
"github.com/docker/docker/pkg/stringid"
|
2019-08-05 10:37:47 -04:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
2017-07-26 17:42:13 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2015-11-18 17:18:07 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// noFallbackMinVersion is the minimum version for which v1compatibility
|
|
|
|
// information will not be marshaled through the Image struct to remove
|
|
|
|
// blank fields.
|
2016-04-19 10:56:54 -04:00
|
|
|
var noFallbackMinVersion = "1.8.3"
|
2015-11-18 17:18:07 -05:00
|
|
|
|
|
|
|
// HistoryFromConfig creates a History struct from v1 configuration JSON
|
|
|
|
func HistoryFromConfig(imageJSON []byte, emptyLayer bool) (image.History, error) {
|
|
|
|
h := image.History{}
|
|
|
|
var v1Image image.V1Image
|
|
|
|
if err := json.Unmarshal(imageJSON, &v1Image); err != nil {
|
|
|
|
return h, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return image.History{
|
|
|
|
Author: v1Image.Author,
|
|
|
|
Created: v1Image.Created,
|
2016-02-29 06:28:37 -05:00
|
|
|
CreatedBy: strings.Join(v1Image.ContainerConfig.Cmd, " "),
|
2015-11-18 17:18:07 -05:00
|
|
|
Comment: v1Image.Comment,
|
|
|
|
EmptyLayer: emptyLayer,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateID creates an ID from v1 image, layerID and parent ID.
|
|
|
|
// Used for backwards compatibility with old clients.
|
|
|
|
func CreateID(v1Image image.V1Image, layerID layer.ChainID, parent digest.Digest) (digest.Digest, error) {
|
|
|
|
v1Image.ID = ""
|
|
|
|
v1JSON, err := json.Marshal(v1Image)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
var config map[string]*json.RawMessage
|
|
|
|
if err := json.Unmarshal(v1JSON, &config); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: note that this is slightly incompatible with RootFS logic
|
|
|
|
config["layer_id"] = rawJSON(layerID)
|
|
|
|
if parent != "" {
|
|
|
|
config["parent"] = rawJSON(parent)
|
|
|
|
}
|
|
|
|
|
|
|
|
configJSON, err := json.Marshal(config)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
logrus.Debugf("CreateV1ID %s", configJSON)
|
|
|
|
|
2015-12-08 14:14:02 -05:00
|
|
|
return digest.FromBytes(configJSON), nil
|
2015-11-18 17:18:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// MakeConfigFromV1Config creates an image config from the legacy V1 config format.
|
|
|
|
func MakeConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) ([]byte, error) {
|
|
|
|
var dver struct {
|
|
|
|
DockerVersion string `json:"docker_version"`
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(imageJSON, &dver); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-04-19 10:56:54 -04:00
|
|
|
useFallback := versions.LessThan(dver.DockerVersion, noFallbackMinVersion)
|
2015-11-18 17:18:07 -05:00
|
|
|
|
|
|
|
if useFallback {
|
|
|
|
var v1Image image.V1Image
|
|
|
|
err := json.Unmarshal(imageJSON, &v1Image)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
imageJSON, err = json.Marshal(v1Image)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var c map[string]*json.RawMessage
|
|
|
|
if err := json.Unmarshal(imageJSON, &c); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(c, "id")
|
|
|
|
delete(c, "parent")
|
2016-02-22 14:22:20 -05:00
|
|
|
delete(c, "Size") // Size is calculated from data on disk and is inconsistent
|
2015-11-18 17:18:07 -05:00
|
|
|
delete(c, "parent_id")
|
|
|
|
delete(c, "layer_id")
|
|
|
|
delete(c, "throwaway")
|
|
|
|
|
|
|
|
c["rootfs"] = rawJSON(rootfs)
|
|
|
|
c["history"] = rawJSON(history)
|
|
|
|
|
|
|
|
return json.Marshal(c)
|
|
|
|
}
|
|
|
|
|
2016-12-19 01:45:48 -05:00
|
|
|
// MakeV1ConfigFromConfig creates a legacy V1 image config from an Image struct
|
2015-11-18 17:18:07 -05:00
|
|
|
func MakeV1ConfigFromConfig(img *image.Image, v1ID, parentV1ID string, throwaway bool) ([]byte, error) {
|
|
|
|
// Top-level v1compatibility string should be a modified version of the
|
|
|
|
// image config.
|
|
|
|
var configAsMap map[string]*json.RawMessage
|
|
|
|
if err := json.Unmarshal(img.RawJSON(), &configAsMap); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete fields that didn't exist in old manifest
|
2016-03-16 21:45:40 -04:00
|
|
|
imageType := reflect.TypeOf(img).Elem()
|
|
|
|
for i := 0; i < imageType.NumField(); i++ {
|
|
|
|
f := imageType.Field(i)
|
|
|
|
jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
|
|
|
|
// Parent is handled specially below.
|
|
|
|
if jsonName != "" && jsonName != "parent" {
|
|
|
|
delete(configAsMap, jsonName)
|
|
|
|
}
|
|
|
|
}
|
2015-11-18 17:18:07 -05:00
|
|
|
configAsMap["id"] = rawJSON(v1ID)
|
|
|
|
if parentV1ID != "" {
|
|
|
|
configAsMap["parent"] = rawJSON(parentV1ID)
|
|
|
|
}
|
|
|
|
if throwaway {
|
|
|
|
configAsMap["throwaway"] = rawJSON(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.Marshal(configAsMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
func rawJSON(value interface{}) *json.RawMessage {
|
|
|
|
jsonval, err := json.Marshal(value)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return (*json.RawMessage)(&jsonval)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateID checks whether an ID string is a valid image ID.
|
|
|
|
func ValidateID(id string) error {
|
2016-12-22 16:26:30 -05:00
|
|
|
return stringid.ValidateID(id)
|
2015-11-18 17:18:07 -05:00
|
|
|
}
|