github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/builder/builder-next/exporter/writer.go (about) 1 package containerimage 2 3 import ( 4 "context" 5 "encoding/json" 6 "runtime" 7 "time" 8 9 "github.com/moby/buildkit/cache" 10 "github.com/moby/buildkit/util/progress" 11 "github.com/moby/buildkit/util/system" 12 digest "github.com/opencontainers/go-digest" 13 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 14 "github.com/pkg/errors" 15 "github.com/sirupsen/logrus" 16 ) 17 18 // const ( 19 // emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") 20 // ) 21 22 func emptyImageConfig() ([]byte, error) { 23 img := ocispec.Image{ 24 Architecture: runtime.GOARCH, 25 OS: runtime.GOOS, 26 } 27 img.RootFS.Type = "layers" 28 img.Config.WorkingDir = "/" 29 img.Config.Env = []string{"PATH=" + system.DefaultPathEnv} 30 dt, err := json.Marshal(img) 31 return dt, errors.Wrap(err, "failed to create empty image config") 32 } 33 34 func parseHistoryFromConfig(dt []byte) ([]ocispec.History, error) { 35 var config struct { 36 History []ocispec.History 37 } 38 if err := json.Unmarshal(dt, &config); err != nil { 39 return nil, errors.Wrap(err, "failed to unmarshal history from config") 40 } 41 return config.History, nil 42 } 43 44 func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History) ([]byte, error) { 45 m := map[string]json.RawMessage{} 46 if err := json.Unmarshal(dt, &m); err != nil { 47 return nil, errors.Wrap(err, "failed to parse image config for patch") 48 } 49 50 var rootFS ocispec.RootFS 51 rootFS.Type = "layers" 52 rootFS.DiffIDs = append(rootFS.DiffIDs, dps...) 53 54 dt, err := json.Marshal(rootFS) 55 if err != nil { 56 return nil, errors.Wrap(err, "failed to marshal rootfs") 57 } 58 m["rootfs"] = dt 59 60 dt, err = json.Marshal(history) 61 if err != nil { 62 return nil, errors.Wrap(err, "failed to marshal history") 63 } 64 m["history"] = dt 65 66 if _, ok := m["created"]; !ok { 67 var tm *time.Time 68 for _, h := range history { 69 if h.Created != nil { 70 tm = h.Created 71 } 72 } 73 dt, err = json.Marshal(&tm) 74 if err != nil { 75 return nil, errors.Wrap(err, "failed to marshal creation time") 76 } 77 m["created"] = dt 78 } 79 80 dt, err = json.Marshal(m) 81 return dt, errors.Wrap(err, "failed to marshal config after patch") 82 } 83 84 func normalizeLayersAndHistory(diffs []digest.Digest, history []ocispec.History, ref cache.ImmutableRef) ([]digest.Digest, []ocispec.History) { 85 refMeta := getRefMetadata(ref, len(diffs)) 86 var historyLayers int 87 for _, h := range history { 88 if !h.EmptyLayer { 89 historyLayers++ 90 } 91 } 92 if historyLayers > len(diffs) { 93 // this case shouldn't happen but if it does force set history layers empty 94 // from the bottom 95 logrus.Warn("invalid image config with unaccounted layers") 96 historyCopy := make([]ocispec.History, 0, len(history)) 97 var l int 98 for _, h := range history { 99 if l >= len(diffs) { 100 h.EmptyLayer = true 101 } 102 if !h.EmptyLayer { 103 l++ 104 } 105 historyCopy = append(historyCopy, h) 106 } 107 history = historyCopy 108 } 109 110 if len(diffs) > historyLayers { 111 // some history items are missing. add them based on the ref metadata 112 for _, md := range refMeta[historyLayers:] { 113 history = append(history, ocispec.History{ 114 Created: &md.createdAt, 115 CreatedBy: md.description, 116 Comment: "buildkit.exporter.image.v0", 117 }) 118 } 119 } 120 121 var layerIndex int 122 for i, h := range history { 123 if !h.EmptyLayer { 124 if h.Created == nil { 125 h.Created = &refMeta[layerIndex].createdAt 126 } 127 layerIndex++ 128 } 129 history[i] = h 130 } 131 132 return diffs, history 133 } 134 135 type refMetadata struct { 136 description string 137 createdAt time.Time 138 } 139 140 func getRefMetadata(ref cache.ImmutableRef, limit int) []refMetadata { 141 if limit <= 0 { 142 return nil 143 } 144 meta := refMetadata{ 145 description: "created by buildkit", // shouldn't be shown but don't fail build 146 createdAt: time.Now(), 147 } 148 if ref == nil { 149 return append(getRefMetadata(nil, limit-1), meta) 150 } 151 if descr := cache.GetDescription(ref.Metadata()); descr != "" { 152 meta.description = descr 153 } 154 meta.createdAt = cache.GetCreatedAt(ref.Metadata()) 155 p := ref.Parent() 156 if p != nil { 157 defer p.Release(context.TODO()) 158 } 159 return append(getRefMetadata(p, limit-1), meta) 160 } 161 162 func oneOffProgress(ctx context.Context, id string) func(err error) error { 163 pw, _, _ := progress.FromContext(ctx) 164 now := time.Now() 165 st := progress.Status{ 166 Started: &now, 167 } 168 pw.Write(id, st) 169 return func(err error) error { 170 // TODO: set error on status 171 now := time.Now() 172 st.Completed = &now 173 pw.Write(id, st) 174 pw.Close() 175 return err 176 } 177 }