1package v2 2 3import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/url" 8 "path" 9 "sort" 10 "strconv" 11 "strings" 12 13 utilstrings "github.com/sensu/sensu-go/util/strings" 14) 15 16const ( 17 // EntitiesResource is the name of this resource type 18 EntitiesResource = "entities" 19 20 // EntityAgentClass is the name of the class given to agent entities. 21 EntityAgentClass = "agent" 22 23 // EntityProxyClass is the name of the class given to proxy entities. 24 EntityProxyClass = "proxy" 25 26 // EntityBackendClass is the name of the class given to backend entities. 27 EntityBackendClass = "backend" 28 29 // Redacted is filled in for fields that contain sensitive information 30 Redacted = "REDACTED" 31) 32 33// DefaultRedactFields contains the default fields to redact 34var DefaultRedactFields = []string{"password", "passwd", "pass", "api_key", 35 "api_token", "access_key", "secret_key", "private_key", "secret"} 36 37// StorePrefix returns the path prefix to this resource in the store 38func (e *Entity) StorePrefix() string { 39 return EntitiesResource 40} 41 42// URIPath returns the path component of an entity URI. 43func (e *Entity) URIPath() string { 44 return path.Join(URLPrefix, "namespaces", url.PathEscape(e.Namespace), EntitiesResource, url.PathEscape(e.Name)) 45} 46 47// Validate returns an error if the entity is invalid. 48func (e *Entity) Validate() error { 49 if err := ValidateName(e.Name); err != nil { 50 return errors.New("entity name " + err.Error()) 51 } 52 53 if err := ValidateName(e.EntityClass); err != nil { 54 return errors.New("entity class " + err.Error()) 55 } 56 57 if e.Namespace == "" { 58 return errors.New("namespace must be set") 59 } 60 61 return nil 62} 63 64// NewEntity creates a new Entity. 65func NewEntity(meta ObjectMeta) *Entity { 66 return &Entity{ObjectMeta: meta} 67} 68 69func redactMap(m map[string]string, redact []string) map[string]string { 70 if len(redact) == 0 { 71 redact = DefaultRedactFields 72 } 73 result := make(map[string]string, len(m)) 74 for k, v := range m { 75 if utilstrings.FoundInArray(k, redact) { 76 result[k] = Redacted 77 } else { 78 result[k] = v 79 } 80 } 81 return result 82} 83 84// GetRedactedEntity redacts the entity according to the entity's Redact fields. 85// A redacted copy is returned. The copy contains pointers to the original's 86// memory, with different Labels and Annotations. 87func (e *Entity) GetRedactedEntity() *Entity { 88 if e == nil { 89 return nil 90 } 91 if e.Labels == nil && e.Annotations == nil { 92 return e 93 } 94 ent := &Entity{} 95 *ent = *e 96 ent.Annotations = redactMap(e.Annotations, e.Redact) 97 ent.Labels = redactMap(e.Labels, e.Redact) 98 return ent 99} 100 101// MarshalJSON implements the json.Marshaler interface. 102func (e *Entity) MarshalJSON() ([]byte, error) { 103 // Redact the entity before marshalling the entity so we don't leak any 104 // sensitive information 105 e = e.GetRedactedEntity() 106 107 type Clone Entity 108 clone := (*Clone)(e) 109 110 return json.Marshal(clone) 111} 112 113// GetEntitySubscription returns the entity subscription, using the format 114// "entity:entityName" 115func GetEntitySubscription(entityName string) string { 116 return fmt.Sprintf("entity:%s", entityName) 117} 118 119// FixtureEntity returns a testing fixture for an Entity object. 120func FixtureEntity(name string) *Entity { 121 return &Entity{ 122 EntityClass: "host", 123 Subscriptions: []string{"linux"}, 124 ObjectMeta: ObjectMeta{ 125 Namespace: "default", 126 Name: name, 127 }, 128 System: System{ 129 Arch: "amd64", 130 }, 131 } 132} 133 134// 135// Sorting 136 137// SortEntitiesByPredicate can be used to sort a given collection using a given 138// predicate. 139func SortEntitiesByPredicate(es []*Entity, fn func(a, b *Entity) bool) sort.Interface { 140 return &entitySorter{entities: es, byFn: fn} 141} 142 143// SortEntitiesByID can be used to sort a given collection of entities by their 144// IDs. 145func SortEntitiesByID(es []*Entity, asc bool) sort.Interface { 146 if asc { 147 return SortEntitiesByPredicate(es, func(a, b *Entity) bool { 148 return a.Name < b.Name 149 }) 150 } 151 return SortEntitiesByPredicate(es, func(a, b *Entity) bool { 152 return a.Name > b.Name 153 }) 154} 155 156// SortEntitiesByLastSeen can be used to sort a given collection of entities by 157// last time each was seen. 158func SortEntitiesByLastSeen(es []*Entity) sort.Interface { 159 return SortEntitiesByPredicate(es, func(a, b *Entity) bool { 160 return a.LastSeen > b.LastSeen 161 }) 162} 163 164type entitySorter struct { 165 entities []*Entity 166 byFn func(a, b *Entity) bool 167} 168 169// Len implements sort.Interface. 170func (s *entitySorter) Len() int { 171 return len(s.entities) 172} 173 174// Swap implements sort.Interface. 175func (s *entitySorter) Swap(i, j int) { 176 s.entities[i], s.entities[j] = s.entities[j], s.entities[i] 177} 178 179// Less implements sort.Interface. 180func (s *entitySorter) Less(i, j int) bool { 181 return s.byFn(s.entities[i], s.entities[j]) 182} 183 184// EntityFields returns a set of fields that represent that resource 185func EntityFields(r Resource) map[string]string { 186 resource := r.(*Entity) 187 return map[string]string{ 188 "entity.name": resource.ObjectMeta.Name, 189 "entity.namespace": resource.ObjectMeta.Namespace, 190 "entity.deregister": strconv.FormatBool(resource.Deregister), 191 "entity.entity_class": resource.EntityClass, 192 "entity.subscriptions": strings.Join(resource.Subscriptions, ","), 193 } 194} 195 196// SetNamespace sets the namespace of the resource. 197func (e *Entity) SetNamespace(namespace string) { 198 e.Namespace = namespace 199} 200