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