Beispiel #1
0
func (c *container) Cleanup(portoConn porto.API) {
	if !c.cleanupEnabled {
		return
	}

	var err error
	if err = portoConn.UnlinkVolume(c.volumePath, c.containerID); err != nil {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).WithError(err).Warnf("Unlink volume %s", c.volumePath)
	} else {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).Debugf("Unlink volume %s successfully", c.volumePath)
	}
	if err = portoConn.UnlinkVolume(c.volumePath, "self"); err != nil {
		apexctx.GetLogger(c.ctx).WithField("id", "self").WithError(err).Warnf("Unlink volume %s", c.volumePath)
	} else {
		apexctx.GetLogger(c.ctx).WithField("id", "self").Debugf("Unlink volume %s successfully", c.volumePath)
	}
	if err = portoConn.Destroy(c.containerID); err != nil {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).WithError(err).Warn("Destroy error")
	} else {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).Debugf("Destroyed")
	}
	if err = os.RemoveAll(c.rootDir); err != nil {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).WithError(err).Warnf("Remove dirs %s", c.rootDir)
	} else {
		apexctx.GetLogger(c.ctx).WithField("id", c.containerID).Debugf("Remove dirs %s successfully", c.rootDir)
	}
}
Beispiel #2
0
func (p portoProfile) applyContainerLimits(ctx context.Context, portoConn porto.API, id string) error {
	limits, ok := p.Profile["container"]
	if !ok {
		apexctx.GetLogger(ctx).WithField("container", id).Info("no container limits")
		return nil
	}

	switch limits := limits.(type) {
	case map[string]interface{}:
		log := apexctx.GetLogger(ctx).WithField("container", id)
		for limit, value := range limits {
			strvalue := fmt.Sprintf("%s", value)
			log.Debugf("apply %s %s", limit, strvalue)
			if err := portoConn.SetProperty(id, limit, strvalue); err != nil {
				return err
			}
		}

		return nil
	default:
		return fmt.Errorf("invalid resources type %T", limits)
	}
}
Beispiel #3
0
func (c *container) start(portoConn porto.API, output io.Writer) (err error) {
	defer apexctx.GetLogger(c.ctx).WithField("id", c.containerID).Trace("start container").Stop(&err)
	c.output = output
	return portoConn.Start(c.containerID)
}
Beispiel #4
0
func newContainer(ctx context.Context, portoConn porto.API, cfg containerConfig, info execInfo) (cnt *container, err error) {
	apexctx.GetLogger(ctx).WithField("container", cfg.ID).Debugf("exec newContainer() with containerConfig: %s; execInfo: %s;", cfg, info)
	volumeProperties := map[string]string{
		"backend": cfg.VolumeBackend,
		"layers":  cfg.Layer,
		"private": "cocaine-app",
	}
	if err = info.applyVolumeLimits(ctx, cfg.ID, volumeProperties); err != nil {
		return nil, err
	}

	volumePath := filepath.Join(cfg.Root, "volume")
	if err = os.MkdirAll(volumePath, 0775); err != nil {
		return nil, err
	}
	defer func(err *error) {
		if *err != nil {
			apexctx.GetLogger(ctx).WithField("container", cfg.ID).Infof("cleaunup unfinished container footprint due to error %v", *err)
			portoConn.UnlinkVolume(volumePath, cfg.ID)
			os.RemoveAll(volumePath)
		}
	}(&err)

	volumeDescription, err := portoConn.CreateVolume(volumePath, volumeProperties)
	apexctx.GetLogger(ctx).WithField("container", cfg.ID).Debugf("create volume with volumeProperties: %s", volumeProperties)
	if err != nil {
		if !isEqualPortoError(err, portorpc.EError_VolumeAlreadyExists) {
			apexctx.GetLogger(ctx).WithError(err).WithField("container", cfg.ID).Error("unable to create volume")
			return nil, err
		}
		apexctx.GetLogger(ctx).WithField("container", cfg.ID).Info("volume already exists")
	} else {
		apexctx.GetLogger(ctx).WithField("container", cfg.ID).Infof("created volume %v", volumeDescription)
	}

	if err = portoConn.Create(cfg.ID); err != nil {
		return nil, err
	}

	if cfg.SetImgURI {
		info.env["image_uri"] = info.portoProfile.Registry() + "/" + info.name
	}

	if err = portoConn.SetProperty(cfg.ID, "bind", formatBinds(&info)); err != nil {
		return nil, err
	}
	if err = portoConn.SetProperty(cfg.ID, "command", formatCommand(info.executable, info.args)); err != nil {
		return nil, err
	}
	if err = portoConn.SetProperty(cfg.ID, "env", formatEnv(info.env)); err != nil {
		return nil, err
	}
	if info.ulimits != "" {
		if err = portoConn.SetProperty(cfg.ID, "ulimit", info.ulimits); err != nil {
			return nil, err
		}
	}
	if cwd := info.Cwd(); cwd != "" {
		if err = portoConn.SetProperty(cfg.ID, "cwd", cwd); err != nil {
			return nil, err
		}
	}
	if err = portoConn.SetProperty(cfg.ID, "net", pickNetwork(string(info.NetworkMode()))); err != nil {
		return nil, err
	}
	if err = portoConn.SetProperty(cfg.ID, "enable_porto", "false"); err != nil {
		return nil, err
	}
	if err = portoConn.SetProperty(cfg.ID, "root", volumePath); err != nil {
		return nil, err
	}
	if err = portoConn.LinkVolume(volumePath, cfg.ID); err != nil {
		return nil, err
	}

	if err = info.portoProfile.applyContainerLimits(ctx, portoConn, cfg.ID); err != nil {
		return nil, err
	}

	cnt = &container{
		ctx: ctx,

		containerID:    cfg.ID,
		rootDir:        cfg.Root,
		volumePath:     volumePath,
		cleanupEnabled: cfg.CleanupEnabled,
		SetImgURI:      cfg.SetImgURI,

		output: ioutil.Discard,
	}
	return cnt, nil
}
Beispiel #5
0
func (b *Box) waitLoop(ctx context.Context) {
	apexctx.GetLogger(ctx).Info("start waitLoop")
	var (
		portoConn porto.API
		err       error
	)

	waitPattern := filepath.Join(b.rootPrefix, "*")

	var waitTimeout = 30 * time.Second

	closed := func(portoConn porto.API) bool {
		select {
		case <-ctx.Done():
			if portoConn != nil {
				portoConn.Close()
			}
			return true
		default:
			return false
		}
	}

LOOP:
	for {
		apexctx.GetLogger(ctx).Info("next iteration of waitLoop")
		if closed(portoConn) {
			return
		}
		// Connect to Porto if we have not connected yet.
		// In case of error: wait either a fixed timeout or closing of Box
		if portoConn == nil {
			apexctx.GetLogger(ctx).Info("waitLoop: connect to Portod")
			portoConn, err = portoConnect()
			if err != nil {
				apexctx.GetLogger(ctx).WithError(err).Warn("unable to connect to Portod")
				select {
				case <-time.After(time.Second):
					continue LOOP
				case <-ctx.Done():
					return
				}
			}
		}

		// * means all containers
		// if no containers dead for waitTimeout, name will be an empty string
		containerName, err := portoConn.Wait([]string{waitPattern}, 30*waitTimeout)
		if err != nil {
			portoConn.Close()
			portoConn = nil
			continue LOOP
		}

		if containerName != "" {
			apexctx.GetLogger(ctx).Infof("Wait reports %s to be dead", containerName)
			b.muContainers.Lock()
			container, ok := b.containers[containerName]
			if ok {
				delete(b.containers, containerName)
			}
			rest := len(b.containers)
			b.muContainers.Unlock()
			if ok {
				if err = container.Kill(); err != nil {
					apexctx.GetLogger(ctx).WithError(err).Errorf("Killing %s error", containerName)
				}
			}

			apexctx.GetLogger(ctx).Infof("%d containers are being tracked now", rest)
		}
	}
}