1package containerimage 2 3import ( 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 22func 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 34func 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 44func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History, cache []byte) ([]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 if cache != nil { 81 dt, err := json.Marshal(cache) 82 if err != nil { 83 return nil, err 84 } 85 m["moby.buildkit.cache.v0"] = dt 86 } 87 88 dt, err = json.Marshal(m) 89 return dt, errors.Wrap(err, "failed to marshal config after patch") 90} 91 92func normalizeLayersAndHistory(diffs []digest.Digest, history []ocispec.History, ref cache.ImmutableRef) ([]digest.Digest, []ocispec.History) { 93 refMeta := getRefMetadata(ref, len(diffs)) 94 var historyLayers int 95 for _, h := range history { 96 if !h.EmptyLayer { 97 historyLayers++ 98 } 99 } 100 if historyLayers > len(diffs) { 101 // this case shouldn't happen but if it does force set history layers empty 102 // from the bottom 103 logrus.Warn("invalid image config with unaccounted layers") 104 historyCopy := make([]ocispec.History, 0, len(history)) 105 var l int 106 for _, h := range history { 107 if l >= len(diffs) { 108 h.EmptyLayer = true 109 } 110 if !h.EmptyLayer { 111 l++ 112 } 113 historyCopy = append(historyCopy, h) 114 } 115 history = historyCopy 116 } 117 118 if len(diffs) > historyLayers { 119 // some history items are missing. add them based on the ref metadata 120 for _, md := range refMeta[historyLayers:] { 121 history = append(history, ocispec.History{ 122 Created: &md.createdAt, 123 CreatedBy: md.description, 124 Comment: "buildkit.exporter.image.v0", 125 }) 126 } 127 } 128 129 var layerIndex int 130 for i, h := range history { 131 if !h.EmptyLayer { 132 if h.Created == nil { 133 h.Created = &refMeta[layerIndex].createdAt 134 } 135 layerIndex++ 136 } 137 history[i] = h 138 } 139 140 return diffs, history 141} 142 143type refMetadata struct { 144 description string 145 createdAt time.Time 146} 147 148func getRefMetadata(ref cache.ImmutableRef, limit int) []refMetadata { 149 if limit <= 0 { 150 return nil 151 } 152 meta := refMetadata{ 153 description: "created by buildkit", // shouldn't be shown but don't fail build 154 createdAt: time.Now(), 155 } 156 if ref == nil { 157 return append(getRefMetadata(nil, limit-1), meta) 158 } 159 if descr := cache.GetDescription(ref.Metadata()); descr != "" { 160 meta.description = descr 161 } 162 meta.createdAt = cache.GetCreatedAt(ref.Metadata()) 163 p := ref.Parent() 164 if p != nil { 165 defer p.Release(context.TODO()) 166 } 167 return append(getRefMetadata(p, limit-1), meta) 168} 169 170func oneOffProgress(ctx context.Context, id string) func(err error) error { 171 pw, _, _ := progress.FromContext(ctx) 172 now := time.Now() 173 st := progress.Status{ 174 Started: &now, 175 } 176 pw.Write(id, st) 177 return func(err error) error { 178 // TODO: set error on status 179 now := time.Now() 180 st.Completed = &now 181 pw.Write(id, st) 182 pw.Close() 183 return err 184 } 185} 186