func (p *process) startContainer(wr io.Writer) error { var startBarier = make(chan struct{}) go p.collectOutput(startBarier, wr) if err := p.client.ContainerStart(p.ctx, p.containerID, ""); err != nil { p.cancellation() return err } isolate.NotifyAboutStart(wr) close(startBarier) return nil }
// Spawn spawns a new process func (b *Box) Spawn(ctx context.Context, config isolate.SpawnConfig, output io.Writer) (proc isolate.Process, err error) { spoolPath := b.spoolPath if val, ok := config.Opts["spool"]; ok { spoolPath = fmt.Sprintf("%s", val) } workDir := filepath.Join(spoolPath, config.Name) var execPath = config.Executable if !filepath.IsAbs(config.Executable) { execPath = filepath.Join(workDir, config.Executable) } packedEnv := make([]string, 0, len(config.Env)) for k, v := range config.Env { packedEnv = append(packedEnv, k+"="+v) } packedArgs := make([]string, 1, len(config.Args)*2+1) packedArgs[0] = filepath.Base(config.Executable) for k, v := range config.Args { packedArgs = append(packedArgs, k, v) } defer apexctx.GetLogger(ctx).WithFields( log.Fields{"name": config.Name, "executable": config.Executable, "workDir": workDir, "execPath": execPath}).Trace("processBox.Spawn").Stop(&err) // Update statistics start := time.Now() spawningQueueSize.Inc(1) if spawningQueueSize.Count() > 10 { spawningQueueSize.Dec(1) return nil, syscall.EAGAIN } err = b.spawnSm.Acquire(ctx) spawningQueueSize.Dec(1) if err != nil { return nil, isolate.ErrSpawningCancelled } defer b.spawnSm.Release() // NOTE: once process was put to the map // its waiter responsibility to Wait for it. // NOTE: No defer here b.mu.Lock() if isolate.IsCancelled(ctx) { b.mu.Unlock() return nil, isolate.ErrSpawningCancelled } newProcStart := time.Now() pr, err := newProcess(ctx, execPath, packedArgs, packedEnv, workDir, output) newProcStarted := time.Now() // Update has lock, so move it out from Hot spot defer procsNewTimer.Update(newProcStarted.Sub(newProcStart)) if err != nil { b.mu.Unlock() procsErroredCounter.Inc(1) return nil, err } b.children[pr.cmd.Process.Pid] = pr.cmd b.mu.Unlock() totalSpawnTimer.UpdateSince(start) isolate.NotifyAboutStart(output) procsCreatedCounter.Inc(1) return pr, err }
// Spawn spawns new Porto container func (b *Box) Spawn(ctx context.Context, config isolate.SpawnConfig, output io.Writer) (isolate.Process, error) { profile := portoProfile{ Profile: config.Opts, } start := time.Now() spawningQueueSize.Inc(1) if spawningQueueSize.Count() > 10 { spawningQueueSize.Dec(1) return nil, syscall.EAGAIN } ei := execInfo{ portoProfile: profile, name: config.Name, executable: config.Executable, ulimits: b.config.DefaultUlimits, args: config.Args, env: config.Env, } ID := b.appGenLabel(config.Name) + "_" + uuid.New() cfg := containerConfig{ Root: filepath.Join(b.config.Containers, ID), ID: b.addRootNamespacePrefix(ID), Layer: b.appLayerName(config.Name), CleanupEnabled: b.config.CleanupEnabled, SetImgURI: b.config.SetImgURI, VolumeBackend: b.config.VolumeBackend, } portoConn, err := portoConnect() if err != nil { return nil, err } defer portoConn.Close() err = b.spawnSM.Acquire(ctx) spawningQueueSize.Dec(1) if err != nil { return nil, isolate.ErrSpawningCancelled } defer b.spawnSM.Release() apexctx.GetLogger(ctx).WithFields(log.Fields{"name": config.Name, "layer": cfg.Layer, "root": cfg.Root, "id": cfg.ID}).Info("Create container") containersCreatedCounter.Inc(1) pr, err := newContainer(ctx, portoConn, cfg, ei) if err != nil { containersErroredCounter.Inc(1) return nil, err } b.muContainers.Lock() b.containers[pr.containerID] = pr b.muContainers.Unlock() if err = pr.start(portoConn, output); err != nil { containersErroredCounter.Inc(1) pr.Cleanup(portoConn) return nil, err } isolate.NotifyAboutStart(output) totalSpawnTimer.UpdateSince(start) return pr, nil }