func()

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
}