in firecracker-control/local.go [96:209]
func (s *local) CreateVM(requestCtx context.Context, req *proto.CreateVMRequest) (*proto.CreateVMResponse, error) {
var err error
id := req.GetVMID()
if err := identifiers.Validate(id); err != nil {
s.logger.WithError(err).Error()
return nil, err
}
ns, err := namespaces.NamespaceRequired(requestCtx)
if err != nil {
err = errors.Wrap(err, "error retrieving namespace of request")
s.logger.WithError(err).Error()
return nil, err
}
s.logger.Debugf("using namespace: %s", ns)
// We determine if there is already a shim managing a VM with the current VMID by attempting
// to listen on the abstract socket address (which is parameterized by VMID). If we get
// EADDRINUSE, then we assume there is already a shim for the VM and return an AlreadyExists error.
shimSocketAddress, err := shim.SocketAddress(requestCtx, s.containerdAddress, id)
if err != nil {
err = errors.Wrap(err, "failed to obtain shim socket address")
s.logger.WithError(err).Error()
return nil, err
}
shimSocket, err := shim.NewSocket(shimSocketAddress)
if shim.SocketEaddrinuse(err) {
return nil, status.Errorf(codes.AlreadyExists, "VM with ID %q already exists (socket: %q)", id, shimSocketAddress)
} else if err != nil {
err = errors.Wrapf(err, "failed to open shim socket at address %q", shimSocketAddress)
s.logger.WithError(err).Error()
return nil, err
}
// If we're here, there is no pre-existing shim for this VMID, so we spawn a new one
if err := os.Mkdir(s.config.ShimBaseDir, 0700); err != nil && !os.IsExist(err) {
s.logger.WithError(err).Error()
return nil, errors.Wrapf(err, "failed to make shim base directory: %s", s.config.ShimBaseDir)
}
shimDir, err := vm.ShimDir(s.config.ShimBaseDir, ns, id)
if err != nil {
err = errors.Wrapf(err, "failed to build shim path")
s.logger.WithError(err).Error()
return nil, err
}
err = shimDir.Mkdir()
if err != nil {
err = errors.Wrapf(err, "failed to create VM dir %q", shimDir.RootPath())
s.logger.WithError(err).Error()
return nil, err
}
defer func() {
if err != nil {
removeErr := os.RemoveAll(shimDir.RootPath())
if removeErr != nil {
s.logger.WithError(removeErr).WithField("path", shimDir.RootPath()).Error("failed to cleanup VM dir")
}
}
}()
// TODO we have to create separate listeners for the fccontrol service and shim service because
// containerd does not currently expose the shim server for us to register the fccontrol service with too.
// This is likely addressable through some relatively small upstream contributions; the following is a stop-gap
// solution until that time.
fcSocketAddress, err := fcShim.FCControlSocketAddress(requestCtx, s.containerdAddress, id)
if err != nil {
err = errors.Wrap(err, "failed to obtain shim socket address")
s.logger.WithError(err).Error()
return nil, err
}
fcSocket, err := shim.NewSocket(fcSocketAddress)
if err != nil {
err = errors.Wrapf(err, "failed to open fccontrol socket at address %q", fcSocketAddress)
s.logger.WithError(err).Error()
return nil, err
}
cmd, err := s.newShim(ns, id, s.containerdAddress, shimSocket, fcSocket)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
cmd.Process.Kill()
}
}()
client, err := s.shimFirecrackerClient(requestCtx, id)
if err != nil {
err = errors.Wrap(err, "failed to create firecracker shim client")
s.logger.WithError(err).Error()
return nil, err
}
defer client.Close()
resp, err := client.CreateVM(requestCtx, req)
if err != nil {
s.logger.WithError(err).Error("shim CreateVM returned error")
return nil, err
}
s.addShim(shimSocketAddress, cmd)
return resp, nil
}