github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/builder/builder-next/worker/worker.go (about) 1 package worker 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/ioutil" 8 nethttp "net/http" 9 "runtime" 10 "time" 11 12 "github.com/containerd/containerd/content" 13 "github.com/containerd/containerd/platforms" 14 "github.com/containerd/containerd/rootfs" 15 "github.com/docker/docker/distribution" 16 distmetadata "github.com/docker/docker/distribution/metadata" 17 "github.com/docker/docker/distribution/xfer" 18 "github.com/docker/docker/image" 19 "github.com/docker/docker/layer" 20 pkgprogress "github.com/docker/docker/pkg/progress" 21 "github.com/moby/buildkit/cache" 22 "github.com/moby/buildkit/cache/metadata" 23 "github.com/moby/buildkit/client" 24 "github.com/moby/buildkit/executor" 25 "github.com/moby/buildkit/exporter" 26 "github.com/moby/buildkit/frontend" 27 gw "github.com/moby/buildkit/frontend/gateway/client" 28 "github.com/moby/buildkit/session" 29 "github.com/moby/buildkit/snapshot" 30 "github.com/moby/buildkit/solver" 31 "github.com/moby/buildkit/solver/llbsolver/ops" 32 "github.com/moby/buildkit/solver/pb" 33 "github.com/moby/buildkit/source" 34 "github.com/moby/buildkit/source/git" 35 "github.com/moby/buildkit/source/http" 36 "github.com/moby/buildkit/source/local" 37 "github.com/moby/buildkit/util/contentutil" 38 "github.com/moby/buildkit/util/progress" 39 digest "github.com/opencontainers/go-digest" 40 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 41 "github.com/pkg/errors" 42 "github.com/sirupsen/logrus" 43 ) 44 45 // Opt defines a structure for creating a worker. 46 type Opt struct { 47 ID string 48 Labels map[string]string 49 GCPolicy []client.PruneInfo 50 SessionManager *session.Manager 51 MetadataStore *metadata.Store 52 Executor executor.Executor 53 Snapshotter snapshot.Snapshotter 54 ContentStore content.Store 55 CacheManager cache.Manager 56 ImageSource source.Source 57 Exporters map[string]exporter.Exporter 58 DownloadManager distribution.RootFSDownloadManager 59 V2MetadataService distmetadata.V2MetadataService 60 Transport nethttp.RoundTripper 61 } 62 63 // Worker is a local worker instance with dedicated snapshotter, cache, and so on. 64 // TODO: s/Worker/OpWorker/g ? 65 type Worker struct { 66 Opt 67 SourceManager *source.Manager 68 } 69 70 // NewWorker instantiates a local worker 71 func NewWorker(opt Opt) (*Worker, error) { 72 sm, err := source.NewManager() 73 if err != nil { 74 return nil, err 75 } 76 77 cm := opt.CacheManager 78 sm.Register(opt.ImageSource) 79 80 gs, err := git.NewSource(git.Opt{ 81 CacheAccessor: cm, 82 MetadataStore: opt.MetadataStore, 83 }) 84 if err == nil { 85 sm.Register(gs) 86 } else { 87 logrus.Warnf("Could not register builder git source: %s", err) 88 } 89 90 hs, err := http.NewSource(http.Opt{ 91 CacheAccessor: cm, 92 MetadataStore: opt.MetadataStore, 93 Transport: opt.Transport, 94 }) 95 if err == nil { 96 sm.Register(hs) 97 } else { 98 logrus.Warnf("Could not register builder http source: %s", err) 99 } 100 101 ss, err := local.NewSource(local.Opt{ 102 SessionManager: opt.SessionManager, 103 CacheAccessor: cm, 104 MetadataStore: opt.MetadataStore, 105 }) 106 if err == nil { 107 sm.Register(ss) 108 } else { 109 logrus.Warnf("Could not register builder local source: %s", err) 110 } 111 112 return &Worker{ 113 Opt: opt, 114 SourceManager: sm, 115 }, nil 116 } 117 118 // ID returns worker ID 119 func (w *Worker) ID() string { 120 return w.Opt.ID 121 } 122 123 // Labels returns map of all worker labels 124 func (w *Worker) Labels() map[string]string { 125 return w.Opt.Labels 126 } 127 128 // Platforms returns one or more platforms supported by the image. 129 func (w *Worker) Platforms() []ocispec.Platform { 130 // does not handle lcow 131 return []ocispec.Platform{platforms.DefaultSpec()} 132 } 133 134 // GCPolicy returns automatic GC Policy 135 func (w *Worker) GCPolicy() []client.PruneInfo { 136 return w.Opt.GCPolicy 137 } 138 139 // LoadRef loads a reference by ID 140 func (w *Worker) LoadRef(id string, hidden bool) (cache.ImmutableRef, error) { 141 var opts []cache.RefOption 142 if hidden { 143 opts = append(opts, cache.NoUpdateLastUsed) 144 } 145 return w.CacheManager.Get(context.TODO(), id, opts...) 146 } 147 148 // ResolveOp converts a LLB vertex into a LLB operation 149 func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) { 150 if baseOp, ok := v.Sys().(*pb.Op); ok { 151 switch op := baseOp.Op.(type) { 152 case *pb.Op_Source: 153 return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w) 154 case *pb.Op_Exec: 155 return ops.NewExecOp(v, op, w.CacheManager, w.Opt.SessionManager, w.MetadataStore, w.Executor, w) 156 case *pb.Op_Build: 157 return ops.NewBuildOp(v, op, s, w) 158 } 159 } 160 return nil, errors.Errorf("could not resolve %v", v) 161 } 162 163 // ResolveImageConfig returns image config for an image 164 func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) { 165 // ImageSource is typically source/containerimage 166 resolveImageConfig, ok := w.ImageSource.(resolveImageConfig) 167 if !ok { 168 return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID()) 169 } 170 return resolveImageConfig.ResolveImageConfig(ctx, ref, opt) 171 } 172 173 // Exec executes a process directly on a worker 174 func (w *Worker) Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { 175 active, err := w.CacheManager.New(ctx, rootFS) 176 if err != nil { 177 return err 178 } 179 defer active.Release(context.TODO()) 180 return w.Executor.Exec(ctx, meta, active, nil, stdin, stdout, stderr) 181 } 182 183 // DiskUsage returns disk usage report 184 func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) { 185 return w.CacheManager.DiskUsage(ctx, opt) 186 } 187 188 // Prune deletes reclaimable build cache 189 func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, info ...client.PruneInfo) error { 190 return w.CacheManager.Prune(ctx, ch, info...) 191 } 192 193 // Exporter returns exporter by name 194 func (w *Worker) Exporter(name string) (exporter.Exporter, error) { 195 exp, ok := w.Exporters[name] 196 if !ok { 197 return nil, errors.Errorf("exporter %q could not be found", name) 198 } 199 return exp, nil 200 } 201 202 // GetRemote returns a remote snapshot reference for a local one 203 func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) { 204 return nil, errors.Errorf("getremote not implemented") 205 } 206 207 // FromRemote converts a remote snapshot reference to a local one 208 func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) { 209 rootfs, err := getLayers(ctx, remote.Descriptors) 210 if err != nil { 211 return nil, err 212 } 213 214 layers := make([]xfer.DownloadDescriptor, 0, len(rootfs)) 215 216 for _, l := range rootfs { 217 // ongoing.add(desc) 218 layers = append(layers, &layerDescriptor{ 219 desc: l.Blob, 220 diffID: layer.DiffID(l.Diff.Digest), 221 provider: remote.Provider, 222 w: w, 223 pctx: ctx, 224 }) 225 } 226 227 defer func() { 228 for _, l := range rootfs { 229 w.ContentStore.Delete(context.TODO(), l.Blob.Digest) 230 } 231 }() 232 233 r := image.NewRootFS() 234 rootFS, release, err := w.DownloadManager.Download(ctx, *r, runtime.GOOS, layers, &discardProgress{}) 235 if err != nil { 236 return nil, err 237 } 238 defer release() 239 240 ref, err := w.CacheManager.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("imported %s", remote.Descriptors[len(remote.Descriptors)-1].Digest))) 241 if err != nil { 242 return nil, err 243 } 244 return ref, nil 245 } 246 247 type discardProgress struct{} 248 249 func (*discardProgress) WriteProgress(_ pkgprogress.Progress) error { 250 return nil 251 } 252 253 // Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) 254 type layerDescriptor struct { 255 provider content.Provider 256 desc ocispec.Descriptor 257 diffID layer.DiffID 258 // ref ctdreference.Spec 259 w *Worker 260 pctx context.Context 261 } 262 263 func (ld *layerDescriptor) Key() string { 264 return "v2:" + ld.desc.Digest.String() 265 } 266 267 func (ld *layerDescriptor) ID() string { 268 return ld.desc.Digest.String() 269 } 270 271 func (ld *layerDescriptor) DiffID() (layer.DiffID, error) { 272 return ld.diffID, nil 273 } 274 275 func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogress.Output) (io.ReadCloser, int64, error) { 276 done := oneOffProgress(ld.pctx, fmt.Sprintf("pulling %s", ld.desc.Digest)) 277 if err := contentutil.Copy(ctx, ld.w.ContentStore, ld.provider, ld.desc); err != nil { 278 return nil, 0, done(err) 279 } 280 done(nil) 281 282 ra, err := ld.w.ContentStore.ReaderAt(ctx, ld.desc) 283 if err != nil { 284 return nil, 0, err 285 } 286 287 return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil 288 } 289 290 func (ld *layerDescriptor) Close() { 291 // ld.is.ContentStore.Delete(context.TODO(), ld.desc.Digest) 292 } 293 294 func (ld *layerDescriptor) Registered(diffID layer.DiffID) { 295 // Cache mapping from this layer's DiffID to the blobsum 296 ld.w.V2MetadataService.Add(diffID, distmetadata.V2Metadata{Digest: ld.desc.Digest}) 297 } 298 299 func getLayers(ctx context.Context, descs []ocispec.Descriptor) ([]rootfs.Layer, error) { 300 layers := make([]rootfs.Layer, len(descs)) 301 for i, desc := range descs { 302 diffIDStr := desc.Annotations["containerd.io/uncompressed"] 303 if diffIDStr == "" { 304 return nil, errors.Errorf("%s missing uncompressed digest", desc.Digest) 305 } 306 diffID, err := digest.Parse(diffIDStr) 307 if err != nil { 308 return nil, err 309 } 310 layers[i].Diff = ocispec.Descriptor{ 311 MediaType: ocispec.MediaTypeImageLayer, 312 Digest: diffID, 313 } 314 layers[i].Blob = ocispec.Descriptor{ 315 MediaType: desc.MediaType, 316 Digest: desc.Digest, 317 Size: desc.Size, 318 } 319 } 320 return layers, nil 321 } 322 323 func oneOffProgress(ctx context.Context, id string) func(err error) error { 324 pw, _, _ := progress.FromContext(ctx) 325 now := time.Now() 326 st := progress.Status{ 327 Started: &now, 328 } 329 pw.Write(id, st) 330 return func(err error) error { 331 // TODO: set error on status 332 now := time.Now() 333 st.Completed = &now 334 pw.Write(id, st) 335 pw.Close() 336 return err 337 } 338 } 339 340 type resolveImageConfig interface { 341 ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) 342 }