This is the 21st day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

In order to strengthen his understanding, Yuan Xiaobai, as usual, combed the following flow chart:

  1. After solver receives the request, it calls the Bridge’s Solve method in the Solve method and begins parsing
  2. The bridge calls Frontend for parsing, which in this case is the dockerfile
  3. In the DockerFile Frontend, the bridge Solve method is called again. The difference this time is that the argument is passed in, and the resolved Definition is passed in
  4. Create the proxy Result, because there is no real build Result at this point, just organize all the objects needed in the middle, such as frontend. Result.ref
  5. Prepare the newResultProxy and return the result res of the first step Solve
  6. After obtaining the Result RES of Frontend Solve, the Result of the proxy Result is loaded
  7. What is actually called is the callBack function pr.cb-proxyresult.callback that creates the code result
  8. The actual request in the proxy result is llbbridge.loadResult
  9. Load is called first in loadResult, and notice that Load, as we saw before, converts Definition to Edge. This is where Edge appears for the first time
  10. The actual Build starts and a new file – Jobs.go appears

The source code is:

res, bi, err := b.builder.Build(ctx, edge)
Copy the code

B is the llbBridge instance. What is b.builder? In the first step, solver’s Solve method calls Bridge’s Solve:

res, err = s.Bridge(j).Solve(ctx, req, sessionID)
Copy the code

Here is where the bridge is instantiated:

func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
   return &llbBridge{
      builder:                   b,
      frontends:                 s.frontends,
      resolveWorker:             s.resolveWorker,
      eachWorker:                s.eachWorker,
      resolveCacheImporterFuncs: s.resolveCacheImporterFuncs,
      cms:                       map[string]solver.CacheManager{},
      sm:                        s.sm,
   }
}
Copy the code

The parameter passed in is called builder, and look at this j:

j, err := s.solver.NewJob(id)
Copy the code

To expand further:

func (jl *Solver) NewJob(id string) (*Job, error) {
   jl.mu.Lock()
   defer jl.mu.Unlock()

   if _, ok := jl.jobs[id]; ok {
      return nil, errors.Errorf("job ID %s exists", id)
   }

   pr, ctx, progressCloser := progress.NewContext(context.Background())
   pw, _, _ := progress.NewFromContext(ctx) // TODO: expose progress.Pipe()

   _, span := trace.NewNoopTracerProvider().Tracer("").Start(ctx, "")
   j := &Job{
      list:           jl,
      pr:             progress.NewMultiReader(pr),
      pw:             pw,
      progressCloser: progressCloser,
      span:           span,
      id:             id,
   }
   jl.jobs[id] = j

   jl.updateCond.Broadcast()

   return j, nil
}
Copy the code

As you can see, there is a list to store the newly created job, and the return j has the corresponding list reference. Broadcast(); jl.updatecond.broadcast (); jl.updatecond.broadcast (); jl.updatecond.broadcast ();

Coming back around, we found the real Build where it started – jobs.build:

func (j *Job) Build(ctx context.Context, e Edge) (CachedResult, BuildInfo, error) {
   if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
      j.span = span
   }

   v, err := j.list.load(e.Vertex, nil, j)
   iferr ! =nil {
      return nil.nil, err
   }
   e.Vertex = v

   res, err := j.list.s.build(ctx, e)
   iferr ! =nil {
      return nil.nil, err
   }

   j.list.mu.Lock()
   defer j.list.mu.Unlock()
   return res, j.walkBuildInfo(ctx, e, make(BuildInfo)), nil
}
Copy the code

Two sentences caught Yuan xiaobai’s attention:

  • j.list.load
  • j.list.s.build

Don’t know what pose to use to properly open jobs load and S. bulild

Next: Dive into the world of Moby Buildkit # 22-Jobs