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 }