func()

in network.go [386:480]


func (cniConf CNIConfiguration) initializeNetNS() (error, []func() error) {
	var cleanupFuncs []func() error

	err := ns.IsNSorErr(cniConf.netNSPath)
	switch err.(type) {
	case nil:
		// if the path already exists and is a netns, just use it as is and return early
		return nil, cleanupFuncs
	case ns.NSPathNotNSErr:
		// if the path exists but isn't a netns, return an error
		return errors.Wrapf(err, "path %q does not appear to be a mounted netns", cniConf.netNSPath), cleanupFuncs
	case ns.NSPathNotExistErr:
		// if the path doesn't exist, continue on to creating a new netns at the path
	default:
		// if something else bad happened return the error
		return errors.Wrapf(err, "failure checking if %q is a mounted netns", cniConf.netNSPath), cleanupFuncs
	}

	// the path doesn't exist, so we need to create a new netns and mount it at the path

	// make sure the parent directory for the path exists
	parentDir := filepath.Dir(cniConf.netNSPath)
	_, err = os.Stat(parentDir)
	if os.IsNotExist(err) {
		err = os.MkdirAll(parentDir, 0600)
		if err != nil {
			return errors.Wrapf(err, "failed to create netns parent dir at %q", parentDir), cleanupFuncs
		}

		cleanupFuncs = append(cleanupFuncs, func() error {
			// Use Remove, not RemoveAll, so we don't clear the directory in the
			// case where something else ended up creating files in the directory
			// concurrently with us.
			err := os.Remove(parentDir)
			if err != nil {
				return errors.Wrapf(err, "failed to remove netns parent dir %q", parentDir)
			}
			return nil
		})
	} else if err != nil {
		return errors.Wrapf(err, "failed to check if netns parent dir exists at %q", parentDir), cleanupFuncs
	}

	// We need a file to exist at the path in order for the bind mount to succeed.
	fd, err := os.OpenFile(cniConf.netNSPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
	if err != nil {
		return errors.Wrapf(err,
			"failed to open new netns path at %q", cniConf.netNSPath), cleanupFuncs
	}
	fd.Close()

	cleanupFuncs = append(cleanupFuncs, func() error {
		err := os.Remove(cniConf.netNSPath)
		if err != nil {
			return errors.Wrapf(err, "failed to remove netns path %q", cniConf.netNSPath)
		}
		return nil
	})

	// Create a new netns and mount it at the destination path. Doing this in a
	// separate OS thread that's discarded at the end of the call is the
	// simplest way to prevent the namespace from leaking to other goroutines.
	doneCh := make(chan error)
	go func() {
		defer close(doneCh)
		// Lock the goroutine to the OS thread but don't ever unlock it. When
		// this func finishes execution the OS thread will just be discarded.
		runtime.LockOSThread()

		// create a new net ns
		err := unix.Unshare(unix.CLONE_NEWNET)
		if err != nil {
			doneCh <- errors.Wrap(err, "failed to unshare netns")
			return
		}

		// mount the new netns at the destination path
		err = unix.Mount("/proc/thread-self/ns/net", cniConf.netNSPath, "none", unix.MS_BIND, "none")
		if err != nil {
			doneCh <- errors.Wrapf(err, "failed to mount netns at path %q", cniConf.netNSPath)
			return
		}

		cleanupFuncs = append(cleanupFuncs, func() error {
			err := unix.Unmount(cniConf.netNSPath, unix.MNT_DETACH)
			if err != nil {
				return errors.Wrapf(err, "failed to unmount netns at %q", cniConf.netNSPath)
			}
			return nil
		})
	}()

	err = <-doneCh
	return err, cleanupFuncs
}