github.com/hernad/nomad@v1.6.112/drivers/shared/capabilities/set.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  // Package capabilities is used for managing sets of linux capabilities.
     5  package capabilities
     6  
     7  import (
     8  	"sort"
     9  	"strings"
    10  )
    11  
    12  type nothing struct{}
    13  
    14  var null = nothing{}
    15  
    16  // Set represents a group linux capabilities, implementing some useful set
    17  // operations, taking care of name normalization, and sentinel value expansions.
    18  //
    19  // Linux capabilities can be expressed in multiple ways when working with docker
    20  // and/or executor, along with Nomad configuration.
    21  //
    22  // Capability names may be upper or lower case, and may or may not be prefixed
    23  // with "CAP_" or "cap_". On top of that, Nomad interprets the special name "all"
    24  // and "ALL" to mean "all capabilities supported by the operating system".
    25  type Set struct {
    26  	data map[string]nothing
    27  }
    28  
    29  // New creates a new Set setting caps as the initial elements.
    30  func New(caps []string) *Set {
    31  	m := make(map[string]nothing, len(caps))
    32  	for _, c := range caps {
    33  		insert(m, c)
    34  	}
    35  	return &Set{data: m}
    36  }
    37  
    38  // Add cap into s.
    39  func (s *Set) Add(cap string) {
    40  	insert(s.data, cap)
    41  }
    42  
    43  func insert(data map[string]nothing, cap string) {
    44  	switch name := normalize(cap); name {
    45  	case "":
    46  	case "all":
    47  		for k, v := range Supported().data {
    48  			data[k] = v
    49  		}
    50  		return
    51  	default:
    52  		data[name] = null
    53  	}
    54  }
    55  
    56  // Remove caps from s.
    57  func (s *Set) Remove(caps []string) {
    58  	for _, c := range caps {
    59  		name := normalize(c)
    60  		if name == "all" {
    61  			s.data = make(map[string]nothing)
    62  			return
    63  		}
    64  		delete(s.data, name)
    65  	}
    66  }
    67  
    68  // Union returns of Set of elements of both s and b.
    69  func (s *Set) Union(b *Set) *Set {
    70  	data := make(map[string]nothing)
    71  	for c := range s.data {
    72  		data[c] = null
    73  	}
    74  	for c := range b.data {
    75  		data[c] = null
    76  	}
    77  	return &Set{data: data}
    78  }
    79  
    80  // Difference returns the Set of elements of b not in s.
    81  func (s *Set) Difference(b *Set) *Set {
    82  	data := make(map[string]nothing)
    83  	for c := range b.data {
    84  		if _, exists := s.data[c]; !exists {
    85  			data[c] = null
    86  		}
    87  	}
    88  	return &Set{data: data}
    89  }
    90  
    91  // Intersect returns the Set of elements in both s and b.
    92  func (s *Set) Intersect(b *Set) *Set {
    93  	data := make(map[string]nothing)
    94  	for c := range s.data {
    95  		if _, exists := b.data[c]; exists {
    96  			data[c] = null
    97  		}
    98  	}
    99  	return &Set{data: data}
   100  }
   101  
   102  // Empty return true if no capabilities exist in s.
   103  func (s *Set) Empty() bool {
   104  	return len(s.data) == 0
   105  }
   106  
   107  // String returns the normalized and sorted string representation of s.
   108  func (s *Set) String() string {
   109  	return strings.Join(s.Slice(false), ", ")
   110  }
   111  
   112  // Slice returns a sorted slice of capabilities in s.
   113  //
   114  // upper - indicates whether to uppercase and prefix capabilities with CAP_
   115  func (s *Set) Slice(upper bool) []string {
   116  	caps := make([]string, 0, len(s.data))
   117  	for c := range s.data {
   118  		if upper {
   119  			c = "CAP_" + strings.ToUpper(c)
   120  		}
   121  		caps = append(caps, c)
   122  	}
   123  	sort.Strings(caps)
   124  	return caps
   125  }
   126  
   127  // linux capabilities are often named in 4 possible ways - upper or lower case,
   128  // and with or without a CAP_ prefix
   129  //
   130  // since we must do comparisons on cap names, always normalize the names before
   131  // letting them into the Set data-structure
   132  func normalize(name string) string {
   133  	spaces := strings.TrimSpace(name)
   134  	lower := strings.ToLower(spaces)
   135  	trim := strings.TrimPrefix(lower, "cap_")
   136  	return trim
   137  }