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