Skip to content

Commit 835f296

Browse files
committed
more build command report information about the target image
1 parent f13a571 commit 835f296

File tree

4 files changed

+204
-64
lines changed

4 files changed

+204
-64
lines changed

internal/app/master/commands/build.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func OnBuild(
6161

6262
cmdReport := report.NewBuildCommand(cmdReportLocation)
6363
cmdReport.State = report.CmdStateStarted
64-
cmdReport.OriginalImage = imageRef
64+
cmdReport.ImageReference = imageRef
6565

6666
fmt.Println("docker-slim[build]: state=started")
6767
fmt.Printf("docker-slim[build]: info=params target=%v continue.mode=%v\n", imageRef, continueAfter.Mode)
@@ -118,10 +118,12 @@ func OnBuild(
118118
strings.Join(imageInspector.DockerfileInfo.AllUsers, ","))
119119
}
120120

121-
if len(imageInspector.DockerfileInfo.Layers) > 0 {
122-
for idx, layerInfo := range imageInspector.DockerfileInfo.Layers {
123-
fmt.Printf("docker-slim[build]: info=image.layers index=%v name='%v' tags='%v'\n",
124-
idx, layerInfo.Name, strings.Join(layerInfo.Tags, ","))
121+
if len(imageInspector.DockerfileInfo.ImageStack) > 0 {
122+
cmdReport.ImageStack = imageInspector.DockerfileInfo.ImageStack
123+
124+
for idx, layerInfo := range imageInspector.DockerfileInfo.ImageStack {
125+
fmt.Printf("docker-slim[build]: info=image.stack index=%v name='%v' id='%v'\n",
126+
idx, layerInfo.FullName, layerInfo.ID)
125127
}
126128
}
127129

@@ -282,15 +284,36 @@ func OnBuild(
282284

283285
if err == nil {
284286
cmdReport.MinifiedBy = float64(imageInspector.ImageInfo.VirtualSize) / float64(newImageInspector.ImageInfo.VirtualSize)
285-
cmdReport.OriginalImageSize = imageInspector.ImageInfo.VirtualSize
286-
cmdReport.OriginalImageSizeHuman = humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize))
287+
288+
cmdReport.SourceImage = report.ImageMetadata{
289+
AllNames: imageInspector.ImageRecordInfo.RepoTags,
290+
ID: imageInspector.ImageRecordInfo.ID,
291+
Size: imageInspector.ImageInfo.VirtualSize,
292+
SizeHuman: humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)),
293+
CreateTime: imageInspector.ImageInfo.Created.UTC().Format(time.RFC3339),
294+
Author: imageInspector.ImageInfo.Author,
295+
DockerVersion: imageInspector.ImageInfo.DockerVersion,
296+
Architecture: imageInspector.ImageInfo.Architecture,
297+
User: imageInspector.ImageInfo.Config.User,
298+
}
299+
300+
if len(imageInspector.ImageRecordInfo.RepoTags) > 0 {
301+
cmdReport.SourceImage.Name = imageInspector.ImageRecordInfo.RepoTags[0]
302+
}
303+
304+
if len(imageInspector.ImageInfo.Config.ExposedPorts) > 0 {
305+
for k := range imageInspector.ImageInfo.Config.ExposedPorts {
306+
cmdReport.SourceImage.ExposedPorts = append(cmdReport.SourceImage.ExposedPorts, string(k))
307+
}
308+
}
309+
287310
cmdReport.MinifiedImageSize = newImageInspector.ImageInfo.VirtualSize
288311
cmdReport.MinifiedImageSizeHuman = humanize.Bytes(uint64(newImageInspector.ImageInfo.VirtualSize))
289312

290313
fmt.Printf("docker-slim[build]: info=results status='MINIFIED BY %.2fX [%v (%v) => %v (%v)]'\n",
291314
cmdReport.MinifiedBy,
292-
cmdReport.OriginalImageSize,
293-
cmdReport.OriginalImageSizeHuman,
315+
cmdReport.SourceImage.Size,
316+
cmdReport.SourceImage.SizeHuman,
294317
cmdReport.MinifiedImageSize,
295318
cmdReport.MinifiedImageSizeHuman)
296319
} else {

internal/app/master/docker/dockerfile/dockerfile.go

Lines changed: 135 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,53 @@ import (
77
"path/filepath"
88
"strconv"
99
"strings"
10+
"time"
1011

1112
log "github.com/Sirupsen/logrus"
1213
"github.com/cloudimmunity/go-dockerclientx"
13-
)
14+
"github.com/dustin/go-humanize"
1415

15-
type Layer struct {
16-
Name string
17-
Tags []string
18-
}
16+
v "github.com/docker-slim/docker-slim/pkg/version"
17+
)
1918

2019
// Info represents the reverse engineered Dockerfile info
2120
type Info struct {
2221
Lines []string
2322
AllUsers []string
2423
ExeUser string
2524
ExposedPorts []string
26-
Layers []Layer
25+
ImageStack []*ImageInfo
26+
}
27+
28+
type ImageInfo struct {
29+
IsTopImage bool `json:"is_top_image"`
30+
ID string `json:"id"`
31+
FullName string `json:"full_name"`
32+
RepoName string `json:"repo_name"`
33+
VersionTag string `json:"version_tag"`
34+
RawTags []string `json:"raw_tags,omitempty"`
35+
CreateTime string `json:"create_time"`
36+
NewSize int64 `json:"new_size"`
37+
NewSizeHuman string `json:"new_size_human"`
38+
BaseImageID string `json:"base_image_id,omitempty"`
39+
Instructions []*InstructionInfo `json:"instructions"`
2740
}
2841

29-
type imageInst struct {
30-
instCmd string
31-
instComment string
32-
instType string
33-
instTime int64
34-
layerImageID string
35-
imageName string
36-
shortTags []string
37-
fullTags []string
42+
type InstructionInfo struct {
43+
Type string `json:"type"`
44+
Time string `json:"time"`
45+
IsNop bool `json:"is_nop"`
46+
IsLocal bool `json:"is_local"`
47+
IntermediateImageID string `json:"intermediate_image_id,omitempty"`
48+
Size int64 `json:"size"`
49+
SizeHuman string `json:"size_human,omitempty"`
50+
CommandSnippet string `json:"command_snippet"`
51+
command string
52+
SystemCommands []string `json:"system_commands,omitempty"`
53+
Comment string `json:"comment,omitempty"`
54+
instPosition string
55+
imageFullName string
56+
RawTags []string `json:"raw_tags,omitempty"`
3857
}
3958

4059
// ReverseDockerfileFromHistory recreates Dockerfile information from container image history
@@ -49,17 +68,26 @@ func ReverseDockerfileFromHistory(apiClient *docker.Client, imageID string) (*In
4968

5069
log.Debugf("\n\nIMAGE HISTORY =>\n%#v\n\n", imageHistory)
5170

52-
var fatImageDockerInstructions []imageInst
71+
var fatImageDockerInstructions []InstructionInfo
72+
var currentImageInfo *ImageInfo
73+
var prevImageID string
5374

5475
imageLayerCount := len(imageHistory)
5576
imageLayerStart := imageLayerCount - 1
77+
startNewImage := true
5678
if imageLayerCount > 0 {
5779
for idx := imageLayerStart; idx >= 0; idx-- {
80+
isNop := false
81+
5882
nopPrefix := "/bin/sh -c #(nop) "
5983
execPrefix := "/bin/sh -c "
6084
rawLine := imageHistory[idx].CreatedBy
6185
var inst string
6286

87+
if strings.Contains(rawLine, "(nop)") {
88+
isNop = true
89+
}
90+
6391
switch {
6492
case len(rawLine) == 0:
6593
inst = "FROM scratch"
@@ -116,60 +144,124 @@ func ReverseDockerfileFromHistory(apiClient *docker.Client, imageID string) (*In
116144
}
117145
}
118146

119-
instInfo := imageInst{
120-
instCmd: inst,
121-
instTime: imageHistory[idx].Created,
122-
layerImageID: imageHistory[idx].ID,
123-
instComment: imageHistory[idx].Comment,
147+
instInfo := InstructionInfo{
148+
IsNop: isNop,
149+
command: cleanInst,
150+
Time: time.Unix(imageHistory[idx].Created, 0).UTC().Format(time.RFC3339),
151+
Comment: imageHistory[idx].Comment,
152+
RawTags: imageHistory[idx].Tags,
153+
Size: imageHistory[idx].Size,
154+
}
155+
156+
instParts := strings.SplitN(cleanInst, " ", 2)
157+
if len(instParts) == 2 {
158+
instInfo.Type = instParts[0]
159+
}
160+
161+
if instInfo.Type == "RUN" {
162+
var cmdParts []string
163+
cmds := strings.Replace(instParts[1], "\\", "", -1)
164+
if strings.Contains(cmds, "&&") {
165+
cmdParts = strings.Split(cmds, "&&")
166+
} else {
167+
cmdParts = strings.Split(cmds, ";")
168+
}
169+
170+
for _, cmd := range cmdParts {
171+
cmd = strings.TrimSpace(cmd)
172+
cmd = strings.Replace(cmd, "\t", "", -1)
173+
cmd = strings.Replace(cmd, "\n", "", -1)
174+
instInfo.SystemCommands = append(instInfo.SystemCommands, cmd)
175+
}
176+
}
177+
178+
if instInfo.Type == "WORKDIR" {
179+
instInfo.SystemCommands = append(instInfo.SystemCommands, fmt.Sprintf("mkdir -p %s", instParts[1]))
180+
}
181+
182+
if len(instInfo.command) > 44 {
183+
instInfo.CommandSnippet = fmt.Sprintf("%s...", instInfo.command[0:44])
184+
} else {
185+
instInfo.CommandSnippet = instInfo.command
186+
}
187+
188+
if instInfo.Size > 0 {
189+
instInfo.SizeHuman = humanize.Bytes(uint64(instInfo.Size))
190+
}
191+
192+
if imageHistory[idx].ID != "<missing>" {
193+
instInfo.IsLocal = true
194+
instInfo.IntermediateImageID = imageHistory[idx].ID
124195
}
125196

126-
instType := "intermediate"
197+
if startNewImage {
198+
startNewImage = false
199+
currentImageInfo = &ImageInfo{
200+
BaseImageID: prevImageID,
201+
NewSize: 0,
202+
}
203+
}
204+
205+
currentImageInfo.NewSize += imageHistory[idx].Size
206+
currentImageInfo.Instructions = append(currentImageInfo.Instructions, &instInfo)
207+
208+
instPosition := "intermediate"
127209
if idx == imageLayerStart {
128-
instType = "first"
210+
instPosition = "first" //first instruction in the list
129211
}
130212

131213
if len(imageHistory[idx].Tags) > 0 {
132-
instType = "last"
214+
instPosition = "last" //last in an image
133215

134-
if tagInfo := strings.Split(imageHistory[idx].Tags[0], ":"); len(tagInfo) > 1 {
135-
instInfo.imageName = tagInfo[0]
136-
}
216+
currentImageInfo.ID = imageHistory[idx].ID
217+
prevImageID = currentImageInfo.ID
137218

138-
instInfo.fullTags = imageHistory[idx].Tags
219+
currentImageInfo.CreateTime = instInfo.Time
220+
currentImageInfo.RawTags = imageHistory[idx].Tags
139221

140-
for _, fullTag := range instInfo.fullTags {
141-
if tagInfo := strings.Split(fullTag, ":"); len(tagInfo) > 1 {
142-
instInfo.shortTags = append(instInfo.shortTags, tagInfo[1])
143-
}
222+
instInfo.imageFullName = imageHistory[idx].Tags[0]
223+
currentImageInfo.FullName = imageHistory[idx].Tags[0]
224+
225+
if tagInfo := strings.Split(imageHistory[idx].Tags[0], ":"); len(tagInfo) > 1 {
226+
currentImageInfo.RepoName = tagInfo[0]
227+
currentImageInfo.VersionTag = tagInfo[1]
144228
}
145229

146-
out.Layers = append(out.Layers, Layer{Name: instInfo.imageName, Tags: instInfo.shortTags})
230+
currentImageInfo.NewSizeHuman = humanize.Bytes(uint64(currentImageInfo.NewSize))
231+
232+
out.ImageStack = append(out.ImageStack, currentImageInfo)
233+
startNewImage = true
147234
}
148235

149-
instInfo.instType = instType
236+
instInfo.instPosition = instPosition
150237

151238
fatImageDockerInstructions = append(fatImageDockerInstructions, instInfo)
152239
}
240+
241+
if currentImageInfo != nil {
242+
currentImageInfo.IsTopImage = true
243+
}
153244
}
154245

155246
for idx, instInfo := range fatImageDockerInstructions {
156-
if instInfo.instType == "first" {
247+
if instInfo.instPosition == "first" {
157248
out.Lines = append(out.Lines, "# new image")
158249
}
159250

160-
out.Lines = append(out.Lines, instInfo.instCmd)
161-
if instInfo.instType == "last" {
251+
out.Lines = append(out.Lines, instInfo.command)
252+
if instInfo.instPosition == "last" {
162253
commentText := fmt.Sprintf("# end of image: %s (id: %s tags: %s)",
163-
instInfo.imageName, instInfo.layerImageID, strings.Join(instInfo.shortTags, ","))
254+
instInfo.imageFullName, instInfo.IntermediateImageID, strings.Join(instInfo.RawTags, ","))
255+
164256
out.Lines = append(out.Lines, commentText)
165257
out.Lines = append(out.Lines, "")
166258
if idx < (len(fatImageDockerInstructions) - 1) {
167259
out.Lines = append(out.Lines, "# new image")
168260
}
169261
}
170262

171-
if instInfo.instComment != "" {
172-
out.Lines = append(out.Lines, "# "+instInfo.instComment)
263+
if instInfo.Comment != "" {
264+
out.Lines = append(out.Lines, "# "+instInfo.Comment)
173265
}
174266

175267
//TODO: use time diff to separate each instruction
@@ -228,6 +320,9 @@ func GenerateFromInfo(location string,
228320
var dfData bytes.Buffer
229321
dfData.WriteString("FROM scratch\n")
230322

323+
dsInfoLabel := fmt.Sprintf("LABEL docker-slim.version=\"%s\"\n", v.Current())
324+
dfData.WriteString(dsInfoLabel)
325+
231326
if len(volumes) > 0 {
232327
var volumeList []string
233328
for volumeName := range volumes {

0 commit comments

Comments
 (0)