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 }