github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/lib/resources/containment_linux.go (about)

     1  //go:build linux
     2  
     3  package resources
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"sync"
    10  
    11  	"github.com/hashicorp/go-hclog"
    12  	"github.com/hashicorp/nomad/client/lib/cgutil"
    13  	"github.com/opencontainers/runc/libcontainer/cgroups"
    14  	"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
    15  	"github.com/opencontainers/runc/libcontainer/configs"
    16  )
    17  
    18  type containment struct {
    19  	lock   sync.RWMutex
    20  	cgroup *configs.Cgroup
    21  	logger hclog.Logger
    22  }
    23  
    24  func Contain(logger hclog.Logger, cgroup *configs.Cgroup) *containment {
    25  	return &containment{
    26  		cgroup: cgroup,
    27  		logger: logger.Named("containment"),
    28  	}
    29  }
    30  
    31  func (c *containment) Apply(pid int) error {
    32  	c.lock.Lock()
    33  	defer c.lock.Unlock()
    34  
    35  	c.logger.Trace("create containment for", "cgroup", c.cgroup, "pid", pid)
    36  
    37  	// for v2 use manager to create and enter the cgroup
    38  	if cgutil.UseV2 {
    39  		mgr, err := fs2.NewManager(c.cgroup, "")
    40  		if err != nil {
    41  			return fmt.Errorf("failed to create v2 cgroup manager for containment: %w", err)
    42  		}
    43  
    44  		// add the pid to the cgroup
    45  		if err = mgr.Apply(pid); err != nil {
    46  			return fmt.Errorf("failed to apply v2 cgroup containment: %w", err)
    47  		}
    48  
    49  		// in v2 it is important to set the device resource configuration
    50  		if err = mgr.Set(c.cgroup.Resources); err != nil {
    51  			return fmt.Errorf("failed to set v2 cgroup resources: %w", err)
    52  		}
    53  
    54  		return nil
    55  	}
    56  
    57  	// for v1 a random cgroup was created already; just enter it
    58  	if err := cgroups.EnterPid(map[string]string{"freezer": c.cgroup.Path}, pid); err != nil {
    59  		return fmt.Errorf("failed to add pid to v1 cgroup: %w", err)
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  func (c *containment) Cleanup() error {
    66  	c.lock.Lock()
    67  	defer c.lock.Unlock()
    68  
    69  	// the current pid is of the executor, who manages the task process cleanup
    70  	executorPID := os.Getpid()
    71  	c.logger.Trace("cleanup on", "cgroup", c.cgroup, "executor_pid", executorPID)
    72  
    73  	// destroy the task processes
    74  	destroyer := cgutil.NewGroupKiller(c.logger, executorPID)
    75  	return destroyer.KillGroup(c.cgroup)
    76  }
    77  
    78  func (c *containment) GetPIDs() PIDs {
    79  	c.lock.Lock()
    80  	defer c.lock.Unlock()
    81  
    82  	m := make(PIDs)
    83  	if c.cgroup == nil {
    84  		return m
    85  	}
    86  
    87  	// get the cgroup path under containment
    88  	var path string
    89  	if cgutil.UseV2 {
    90  		path = filepath.Join(cgutil.CgroupRoot, c.cgroup.Path)
    91  	} else {
    92  		path = c.cgroup.Path
    93  	}
    94  
    95  	// find the pids in the cgroup under containment
    96  	pids, err := cgroups.GetAllPids(path)
    97  	if err != nil {
    98  		c.logger.Debug("failed to get pids", "cgroup", c.cgroup, "error", err)
    99  		return m
   100  	}
   101  
   102  	for _, pid := range pids {
   103  		m[pid] = NewPID(pid)
   104  	}
   105  
   106  	return m
   107  }