-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimagev1.go
156 lines (132 loc) · 4.06 KB
/
imagev1.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package v1
import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
)
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 = "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 := versions.LessThan(dver.DockerVersion, 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
}