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  }