Skip to content
This repository has been archived by the owner on Feb 27, 2020. It is now read-only.

Implement video feature in docker engine #383

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

walac
Copy link
Contributor

@walac walac commented May 21, 2018

No description provided.

@coveralls
Copy link

coveralls commented May 21, 2018

Coverage Status

Coverage decreased (-0.3%) to 45.396% when pulling e655143 on walac:loopback-video into 4d93412 on taskcluster:master.

@@ -100,6 +121,13 @@ func (e *engine) NewSandboxBuilder(options engines.SandboxOptions) (engines.Sand
var p payloadType
schematypes.MustValidateAndMap(e.PayloadSchema(), options.Payload, &p)

p.Devices = funk.UniqString(p.Devices)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for deduplication? If so, just add Unique: true in the JSON schema for array...

Description: "List of host devices required.",
Items: schematypes.StringEnum{
Options: []string{
"video",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just make the option of video be dependent on whether devices are enabled or not... anyways, nit... I'm still not sure what's ideal in either case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would suggest that we do:

devices: [
  {type: 'video'},
],

That way, if in some future we decide we want additional options, we can add them to the object...

@@ -43,6 +45,20 @@ func newSandbox(sb *sandboxBuilder, image *docker.Image) (*sandbox, error) {
return nil, errors.Wrap(err, "docker.CreateNetwork failed")
}

devices := []docker.Device{}
var dev *device
if funk.InStrings(sb.payload.Devices, "video") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for loop + a switch is likely better...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe when we add more options.

return nil, errors.Wrap(err, "Failed to call filepath.Glob function")
}

r := regexp.MustCompile("/dev/video[0-9]+")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want to make the devices in here? Also I think a devices sub-package would be neat.

That way we can hide some of the locking and internals away, and the engine just deals with a device which is something that has a path... and is created when the array from payload is passed to the device manager...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know if I followed the idea, but let's keep things simple for now.

@walac walac changed the title [WIP] Implement video feature in docker engine Implement video feature in docker engine May 23, 2018
@walac walac requested a review from jonasfj May 23, 2018 21:38
Copy link
Contributor

@jonasfj jonasfj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the test case :)

And this doesn't have to be a glorified as I suggest in my comments... I just think it's not that hard to isolate the various aspects, so that things will be extensible.

But at a minimum we should fix missing locking and ensure we do life-cycle management correctly.

}

func (d *videoDeviceManager) claim() *device {
for i := range d.devices {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

locking?

func (d *videoDeviceManager) claim() *device {
for i := range d.devices {
if !d.devices[i].claimed {
return &d.devices[i]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning a reference to an object stored in a list... it's probably better to make a list pointers to devices..

Just so we don't have to think too much about happens when if we mutate the device... or something like that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At no point do you set, dev.claimed = true

@@ -277,5 +298,9 @@ func (s *sandbox) dispose() error {
if hasErr {
return runtime.ErrNonFatalInternalError
}

if s.videoDev != nil {
s.videoDev.claimed = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably best to decide what is ecapsulated where... You have a method videoDeviceManager.release

@@ -87,6 +106,7 @@ func newSandbox(sb *sandboxBuilder) (*sandbox, error) {
storage, err := sb.e.Environment.TemporaryStorage.NewFolder()
if err != nil {
imageHandle.Release()
sb.e.video.release(dev)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably too soon to release, the container still exists...

devices []device
}

func newVideoDeviceManager() (*videoDeviceManager, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest something like:

package dockerengine

import (
	"errors"
	"sync"

	"github.com/taskcluster/taskcluster-worker/runtime/atomics"
)

type deviceHandle interface {
	Path() string
	Release()
}

type videoDeviceManager struct {
	m       sync.Mutex
	devices []string
}

// newVideoDeviceManager creates a new videoDeviceManager
func newVideoDeviceManager() (*videoDeviceManager, error) {
	// TODO: Find or create devices
	devices := []string{"/dev/video0"}
	return &videoDeviceManager{devices: devices}, nil
}

// GetVideoDevice returns an idle video device
func (vdm *videoDeviceManager) GetVideoDevice() deviceHandle {
	vdm.m.Lock()
	defer vdm.m.Unlock()

	// ensure we have enough devices
	if len(vdm.devices) < 1 {
		panic(errors.New("insufficent number of video devices available"))
	}

	// Create a device handle, ensuring we can't release twice
	vdh := &videoDeviceHandle{owner: vdm}
	vdh.path, vdm.devices = vdm.devices[0], vdm.devices[1:]

	return vdh
}

// Dispose releases all resources assocated with the videoDeviceManager
func (vdm *videoDeviceManager) Dispose() error {
	vdm.m.Lock()
	defer vdm.m.Unlock()

	return nil // TODO: remove devices if any was created
}

type videoDeviceHandle struct {
	owner    *videoDeviceManager
	path     string
	released atomics.Once
}

func (vdh *videoDeviceHandle) Path() string {
	if vdh.released.IsDone() {
		panic(errors.New("videoDeviceHandle.Path() was called after release"))
	}
	return vdh.path
}

func (vdh *videoDeviceHandle) Release() string {
	ok := vdh.released.Do(func() {
		vdh.owner.m.Lock()
		defer vdh.owner.m.Unlock()

		vdh.owner.devices = append(vdh.owner.devices, vdh.path)
	})
	if !ok {
		debug("WARNING: harmless double release() of videoDeviceHandle")
	}
}

In the sandbox and resultset code you just release() the deviceHandle the same way we release imageHandle and pass it around the same way.

While techincally all this is in the same package, I only use methods starting with capital outside this file... the only exception being: newVideoDeviceManager(). This makes it easier to track life-cycles, and it is a motivation for moving device management into separate package, though that's a lot less important..

Description: "List of host devices required.",
Items: schematypes.StringEnum{
Options: []string{
"video",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would suggest that we do:

devices: [
  {type: 'video'},
],

That way, if in some future we decide we want additional options, we can add them to the object...

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants