-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathnew.go
135 lines (121 loc) · 3.6 KB
/
new.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
package local
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/buildpacks/imgutil"
)
// NewImage returns a new image that can be modified and saved to a docker daemon
// via a tarball in legacy format.
func NewImage(repoName string, dockerClient DockerClient, ops ...imgutil.ImageOption) (*Image, error) {
options := &imgutil.ImageOptions{}
for _, op := range ops {
op(options)
}
var err error
options.Platform, err = processPlatformOption(options.Platform, dockerClient)
if err != nil {
return nil, err
}
previousImage, err := processImageOption(options.PreviousImageRepoName, dockerClient, true)
if err != nil {
return nil, err
}
if previousImage.image != nil {
options.PreviousImage = previousImage.image
}
var (
baseIdentifier string
store *Store
)
baseImage, err := processImageOption(options.BaseImageRepoName, dockerClient, false)
if err != nil {
return nil, err
}
if baseImage.image != nil {
options.BaseImage = baseImage.image
baseIdentifier = baseImage.identifier
store = baseImage.layerStore
} else {
store = NewStore(dockerClient)
}
cnbImage, err := imgutil.NewCNBImage(*options)
if err != nil {
return nil, err
}
return &Image{
CNBImageCore: cnbImage,
repoName: repoName,
store: store,
lastIdentifier: baseIdentifier,
daemonOS: options.Platform.OS,
}, nil
}
func defaultPlatform(dockerClient DockerClient) (imgutil.Platform, error) {
daemonInfo, err := dockerClient.ServerVersion(context.Background())
if err != nil {
return imgutil.Platform{}, err
}
return imgutil.Platform{
OS: daemonInfo.Os,
Architecture: daemonInfo.Arch,
}, nil
}
func processPlatformOption(requestedPlatform imgutil.Platform, dockerClient DockerClient) (imgutil.Platform, error) {
dockerPlatform, err := defaultPlatform(dockerClient)
if err != nil {
return imgutil.Platform{}, err
}
if (requestedPlatform == imgutil.Platform{}) {
return dockerPlatform, nil
}
if requestedPlatform.OS != "" && requestedPlatform.OS != dockerPlatform.OS {
return imgutil.Platform{},
fmt.Errorf("invalid os: platform os %q must match the daemon os %q", requestedPlatform.OS, dockerPlatform.OS)
}
return requestedPlatform, nil
}
type imageResult struct {
image v1.Image
identifier string
layerStore *Store
}
func processImageOption(repoName string, dockerClient DockerClient, downloadLayersOnAccess bool) (imageResult, error) {
if repoName == "" {
return imageResult{}, nil
}
inspect, history, err := getInspectAndHistory(repoName, dockerClient)
if err != nil {
return imageResult{}, err
}
if inspect == nil {
return imageResult{}, nil
}
layerStore := NewStore(dockerClient)
v1Image, err := newV1ImageFacadeFromInspect(*inspect, history, layerStore, downloadLayersOnAccess)
if err != nil {
return imageResult{}, err
}
return imageResult{
image: v1Image,
identifier: inspect.ID,
layerStore: layerStore,
}, nil
}
func getInspectAndHistory(repoName string, dockerClient DockerClient) (*types.ImageInspect, []image.HistoryResponseItem, error) {
inspect, _, err := dockerClient.ImageInspectWithRaw(context.Background(), repoName)
if err != nil {
if client.IsErrNotFound(err) {
return nil, nil, nil
}
return nil, nil, fmt.Errorf("inspecting image %q: %w", repoName, err)
}
history, err := dockerClient.ImageHistory(context.Background(), repoName)
if err != nil {
return nil, nil, fmt.Errorf("get history for image %q: %w", repoName, err)
}
return &inspect, history, nil
}