open-nomad/drivers/shared/capabilities/set.go
Seth Hoenig f64baec276 docs: update docs for linux capabilities in exec/java/docker drivers
Update docs for allow_caps, cap_add, cap_drop in exec/java/docker driver
pages. Also update upgrade guide with guidance on new default linux
capabilities for exec and java drivers.
2021-05-17 12:37:40 -06:00

135 lines
3.1 KiB
Go

// Package capabilities is used for managing sets of linux capabilities.
package capabilities
import (
"sort"
"strings"
)
type nothing struct{}
var null = nothing{}
// Set represents a group linux capabilities, implementing some useful set
// operations, taking care of name normalization, and sentinel value expansions.
//
// Linux capabilities can be expressed in multiple ways when working with docker
// and/or executor, along with Nomad configuration.
//
// Capability names may be upper or lower case, and may or may not be prefixed
// with "CAP_" or "cap_". On top of that, Nomad interprets the special name "all"
// and "ALL" to mean "all capabilities supported by the operating system".
type Set struct {
data map[string]nothing
}
// New creates a new Set setting caps as the initial elements.
func New(caps []string) *Set {
m := make(map[string]nothing, len(caps))
for _, c := range caps {
insert(m, c)
}
return &Set{data: m}
}
// Add cap into s.
func (s *Set) Add(cap string) {
insert(s.data, cap)
}
func insert(data map[string]nothing, cap string) {
switch name := normalize(cap); name {
case "":
case "all":
for k, v := range Supported().data {
data[k] = v
}
return
default:
data[name] = null
}
}
// Remove caps from s.
func (s *Set) Remove(caps []string) {
for _, c := range caps {
name := normalize(c)
if name == "all" {
s.data = make(map[string]nothing)
return
}
delete(s.data, name)
}
}
// Union returns of Set of elements of both s and b.
func (s *Set) Union(b *Set) *Set {
data := make(map[string]nothing)
for c := range s.data {
data[c] = null
}
for c := range b.data {
data[c] = null
}
return &Set{data: data}
}
// Difference returns the Set of elements of b not in s.
func (s *Set) Difference(b *Set) *Set {
data := make(map[string]nothing)
for c := range b.data {
if _, exists := s.data[c]; !exists {
data[c] = null
}
}
return &Set{data: data}
}
// Intersect returns the Set of elements in both s and b.
func (s *Set) Intersect(b *Set) *Set {
data := make(map[string]nothing)
for c := range s.data {
if _, exists := b.data[c]; exists {
data[c] = null
}
}
return &Set{data: data}
}
// Empty return true if no capabilities exist in s.
func (s *Set) Empty() bool {
return len(s.data) == 0
}
// String returns the normalized and sorted string representation of s.
func (s *Set) String() string {
return strings.Join(s.Slice(false), ", ")
}
// Slice returns a sorted slice of capabilities in s.
//
// upper - indicates whether to uppercase and prefix capabilities with CAP_
func (s *Set) Slice(upper bool) []string {
caps := make([]string, 0, len(s.data))
for c := range s.data {
if upper {
c = "CAP_" + strings.ToUpper(c)
}
caps = append(caps, c)
}
sort.Strings(caps)
return caps
}
// linux capabilities are often named in 4 possible ways - upper or lower case,
// and with or without a CAP_ prefix
//
// since we must do comparisons on cap names, always normalize the names before
// letting them into the Set data-structure
func normalize(name string) string {
spaces := strings.TrimSpace(name)
lower := strings.ToLower(spaces)
trim := strings.TrimPrefix(lower, "cap_")
return trim
}