Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
grpc: Handle hotplugged devices by updating spec info
Browse files Browse the repository at this point in the history
When adding a host device to the container by using things like
"docker run --device /dev/sda1:/dev/blk1", the runtime will not
be able to anticipate the major/minor numbers for the device
being hotplugged into the VM. This means the agent has to update
the list of devices provided through the spec, by relying on the
list of Storages.

This commit allows the agent to analyze a list of mounts identified
as devices by using the field Driver of the Storage structure. This
way, it knows when it should try to find major/minor pair related
to the devices listed in the spec. Then, it updates this list in the
spec structure so that proper bind mounts between the VM and the
container are performed.

Fixes #132

Signed-off-by: Sebastien Boeuf <[email protected]>
  • Loading branch information
Sebastien Boeuf committed Feb 3, 2018
1 parent a371c5d commit 2c3dec9
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 9 deletions.
58 changes: 58 additions & 0 deletions grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,60 @@ func (a *agentGRPC) updateContainerConfig(spec *specs.Spec, config *configs.Conf
return a.updateContainerConfigPrivileges(spec, config)
}

func updateGRPCSpecDevices(mounts []*pb.Storage, spec *pb.Spec) error {
if spec.Linux == nil || len(spec.Linux.Devices) == 0 {
// No reason to go further in this case since we won't be
// able to modify any device from the spec.
return nil
}

for _, mnt := range mounts {
if mnt == nil {
continue
}

if mnt.Driver == "" {
continue
}

// In case we have a device identified by the driver field,
// we want first to make sure it shows up properly, and then
// we need to retrieve its device info (such as major and
// minor numbers), useful to update the device provided
// through the OCI specification.
if err := waitForDevice(mnt.Source); err != nil {
return err
}

stat := syscall.Stat_t{}
if err := syscall.Stat(mnt.Source, &stat); err != nil {
return err
}

major := int64(stat.Rdev/256)
minor := int64(stat.Rdev%256)

agentLog.Infof("Device: %s, Major: %d, Minor: %d", mnt.Source, major, minor)

// Update the spec
updated := false
for idx, device := range spec.Linux.Devices {
if device.Path == mnt.MountPoint {
spec.Linux.Devices[idx].Major = major
spec.Linux.Devices[idx].Minor = minor
updated = true
break
}
}

if !updated {
return fmt.Errorf("Should have found a matching device in the spec")
}
}

return nil
}

func (a *agentGRPC) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (*gpb.Empty, error) {
if a.sandbox.running == false {
return emptyResp, fmt.Errorf("Sandbox not started, impossible to run a new container")
Expand All @@ -342,6 +396,10 @@ func (a *agentGRPC) CreateContainer(ctx context.Context, req *pb.CreateContainer
return emptyResp, err
}

if err := updateGRPCSpecDevices(req.Storages, req.OCI); err != nil {
return emptyResp, err
}

// Convert the spec to an actual OCI specification structure.
ociSpec, err := pb.GRPCtoOCI(req.OCI)
if err != nil {
Expand Down
14 changes: 5 additions & 9 deletions mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,11 @@ func addMounts(mounts []*pb.Storage) ([]string, error) {
continue
}

// Consider all other fs types as being hotpluggable, meaning
// we should wait for them to show up before trying to mount
// them.
if mnt.Fstype != "" &&
mnt.Fstype != "bind" &&
mnt.Fstype != type9pFs {
if err := waitForDevice(mnt.Source); err != nil {
return nil, err
}
// The field Driver is used to differentiate a device from
// a mount. Nothing needs to be done for a device in this
// function.
if mnt.Driver != "" {
continue
}

flags, options, err := parseMountFlagsAndOptions(mnt.Options)
Expand Down

0 comments on commit 2c3dec9

Please sign in to comment.