mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
194eaa5c0f
These fields are needed to specify the exact version of Windows that an image can run on. They may be useful for other platforms in the future. This also changes image.store.Create to validate that the loaded image is supported on the current machine. This change affects Linux as well, since it now validates the architecture and OS fields. Signed-off-by: John Starks <jostarks@microsoft.com>
156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
package v1
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/layer"
|
|
"github.com/docker/docker/pkg/version"
|
|
)
|
|
|
|
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
|
|
|
// noFallbackMinVersion is the minimum version for which v1compatibility
|
|
// information will not be marshaled through the Image struct to remove
|
|
// blank fields.
|
|
var noFallbackMinVersion = version.Version("1.8.3")
|
|
|
|
// 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,
|
|
CreatedBy: strings.Join(v1Image.ContainerConfig.Cmd, " "),
|
|
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)
|
|
|
|
return digest.FromBytes(configJSON), nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
useFallback := version.Version(dver.DockerVersion).LessThan(noFallbackMinVersion)
|
|
|
|
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")
|
|
delete(c, "Size") // Size is calculated from data on disk and is inconsistent
|
|
delete(c, "parent_id")
|
|
delete(c, "layer_id")
|
|
delete(c, "throwaway")
|
|
|
|
c["rootfs"] = rawJSON(rootfs)
|
|
c["history"] = rawJSON(history)
|
|
|
|
return json.Marshal(c)
|
|
}
|
|
|
|
// MakeV1ConfigFromConfig creates an legacy V1 image config from an Image struct
|
|
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
|
|
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)
|
|
}
|
|
}
|
|
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 {
|
|
if ok := validHex.MatchString(id); !ok {
|
|
return fmt.Errorf("image ID %q is invalid", id)
|
|
}
|
|
return nil
|
|
}
|