github.com/bigcommerce/nomad@v0.9.3-bc/drivers/shared/executor/executor_universal_linux.go (about)

     1  package executor
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/user"
     7  	"strconv"
     8  	"syscall"
     9  
    10  	multierror "github.com/hashicorp/go-multierror"
    11  	"github.com/hashicorp/nomad/helper"
    12  	"github.com/opencontainers/runc/libcontainer/cgroups"
    13  	cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
    14  	lconfigs "github.com/opencontainers/runc/libcontainer/configs"
    15  )
    16  
    17  // runAs takes a user id as a string and looks up the user, and sets the command
    18  // to execute as that user.
    19  func (e *UniversalExecutor) runAs(userid string) error {
    20  	u, err := user.Lookup(userid)
    21  	if err != nil {
    22  		return fmt.Errorf("Failed to identify user %v: %v", userid, err)
    23  	}
    24  
    25  	// Get the groups the user is a part of
    26  	gidStrings, err := u.GroupIds()
    27  	if err != nil {
    28  		return fmt.Errorf("Unable to lookup user's group membership: %v", err)
    29  	}
    30  
    31  	gids := make([]uint32, len(gidStrings))
    32  	for _, gidString := range gidStrings {
    33  		u, err := strconv.Atoi(gidString)
    34  		if err != nil {
    35  			return fmt.Errorf("Unable to convert user's group to int %s: %v", gidString, err)
    36  		}
    37  
    38  		gids = append(gids, uint32(u))
    39  	}
    40  
    41  	// Convert the uid and gid
    42  	uid, err := strconv.ParseUint(u.Uid, 10, 32)
    43  	if err != nil {
    44  		return fmt.Errorf("Unable to convert userid to uint32: %s", err)
    45  	}
    46  	gid, err := strconv.ParseUint(u.Gid, 10, 32)
    47  	if err != nil {
    48  		return fmt.Errorf("Unable to convert groupid to uint32: %s", err)
    49  	}
    50  
    51  	// Set the command to run as that user and group.
    52  	if e.childCmd.SysProcAttr == nil {
    53  		e.childCmd.SysProcAttr = &syscall.SysProcAttr{}
    54  	}
    55  	if e.childCmd.SysProcAttr.Credential == nil {
    56  		e.childCmd.SysProcAttr.Credential = &syscall.Credential{}
    57  	}
    58  	e.childCmd.SysProcAttr.Credential.Uid = uint32(uid)
    59  	e.childCmd.SysProcAttr.Credential.Gid = uint32(gid)
    60  	e.childCmd.SysProcAttr.Credential.Groups = gids
    61  
    62  	e.logger.Debug("setting process user", "user", uid, "group", gid, "additional_groups", gids)
    63  
    64  	return nil
    65  }
    66  
    67  // configureResourceContainer configured the cgroups to be used to track pids
    68  // created by the executor
    69  func (e *UniversalExecutor) configureResourceContainer(pid int) error {
    70  	cfg := &lconfigs.Config{
    71  		Cgroups: &lconfigs.Cgroup{
    72  			Resources: &lconfigs.Resources{
    73  				AllowAllDevices: helper.BoolToPtr(true),
    74  			},
    75  		},
    76  	}
    77  
    78  	configureBasicCgroups(cfg)
    79  	e.resConCtx.groups = cfg.Cgroups
    80  	return cgroups.EnterPid(cfg.Cgroups.Paths, pid)
    81  }
    82  
    83  // DestroyCgroup kills all processes in the cgroup and removes the cgroup
    84  // configuration from the host. This function is idempotent.
    85  func DestroyCgroup(groups *lconfigs.Cgroup, executorPid int) error {
    86  	mErrs := new(multierror.Error)
    87  	if groups == nil {
    88  		return fmt.Errorf("Can't destroy: cgroup configuration empty")
    89  	}
    90  
    91  	// Move the executor into the global cgroup so that the task specific
    92  	// cgroup can be destroyed.
    93  	path, err := cgroups.GetInitCgroupPath("freezer")
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	if err := cgroups.EnterPid(map[string]string{"freezer": path}, executorPid); err != nil {
    99  		return err
   100  	}
   101  
   102  	// Freeze the Cgroup so that it can not continue to fork/exec.
   103  	groups.Resources.Freezer = lconfigs.Frozen
   104  	freezer := cgroupFs.FreezerGroup{}
   105  	if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil {
   106  		return err
   107  	}
   108  
   109  	var procs []*os.Process
   110  	pids, err := cgroups.GetAllPids(groups.Paths[freezer.Name()])
   111  	if err != nil {
   112  		multierror.Append(mErrs, fmt.Errorf("error getting pids: %v", err))
   113  
   114  		// Unfreeze the cgroup.
   115  		groups.Resources.Freezer = lconfigs.Thawed
   116  		freezer := cgroupFs.FreezerGroup{}
   117  		if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil {
   118  			multierror.Append(mErrs, fmt.Errorf("failed to unfreeze cgroup: %v", err))
   119  			return mErrs.ErrorOrNil()
   120  		}
   121  	}
   122  
   123  	// Kill the processes in the cgroup
   124  	for _, pid := range pids {
   125  		proc, err := os.FindProcess(pid)
   126  		if err != nil {
   127  			multierror.Append(mErrs, fmt.Errorf("error finding process %v: %v", pid, err))
   128  			continue
   129  		}
   130  
   131  		procs = append(procs, proc)
   132  		if e := proc.Kill(); e != nil {
   133  			multierror.Append(mErrs, fmt.Errorf("error killing process %v: %v", pid, e))
   134  		}
   135  	}
   136  
   137  	// Unfreeze the cgroug so we can wait.
   138  	groups.Resources.Freezer = lconfigs.Thawed
   139  	if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil {
   140  		multierror.Append(mErrs, fmt.Errorf("failed to unfreeze cgroup: %v", err))
   141  		return mErrs.ErrorOrNil()
   142  	}
   143  
   144  	// Wait on the killed processes to ensure they are cleaned up.
   145  	for _, proc := range procs {
   146  		// Don't capture the error because we expect this to fail for
   147  		// processes we didn't fork.
   148  		proc.Wait()
   149  	}
   150  
   151  	// Remove the cgroup.
   152  	if err := cgroups.RemovePaths(groups.Paths); err != nil {
   153  		multierror.Append(mErrs, fmt.Errorf("failed to delete the cgroup directories: %v", err))
   154  	}
   155  	return mErrs.ErrorOrNil()
   156  }