hub.fastgit.org/hashicorp/consul.git@v1.4.5/agent/user_event.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/hashicorp/consul/agent/structs" 8 "github.com/hashicorp/go-uuid" 9 ) 10 11 const ( 12 // userEventMaxVersion is the maximum protocol version we understand 13 userEventMaxVersion = 1 14 15 // remoteExecName is the event name for a remote exec command 16 remoteExecName = "_rexec" 17 ) 18 19 // UserEventParam is used to parameterize a user event 20 type UserEvent struct { 21 // ID of the user event. Automatically generated. 22 ID string 23 24 // Name of the event 25 Name string `codec:"n"` 26 27 // Optional payload 28 Payload []byte `codec:"p,omitempty"` 29 30 // NodeFilter is a regular expression to filter on nodes 31 NodeFilter string `codec:"nf,omitempty"` 32 33 // ServiceFilter is a regular expression to filter on services 34 ServiceFilter string `codec:"sf,omitempty"` 35 36 // TagFilter is a regular expression to filter on tags of a service, 37 // must be provided with ServiceFilter 38 TagFilter string `codec:"tf,omitempty"` 39 40 // Version of the user event. Automatically generated. 41 Version int `codec:"v"` 42 43 // LTime is the lamport time. Automatically generated. 44 LTime uint64 `codec:"-"` 45 } 46 47 // validateUserEventParams is used to sanity check the inputs 48 func validateUserEventParams(params *UserEvent) error { 49 // Validate the inputs 50 if params.Name == "" { 51 return fmt.Errorf("User event missing name") 52 } 53 if params.TagFilter != "" && params.ServiceFilter == "" { 54 return fmt.Errorf("Cannot provide tag filter without service filter") 55 } 56 if params.NodeFilter != "" { 57 if _, err := regexp.Compile(params.NodeFilter); err != nil { 58 return fmt.Errorf("Invalid node filter: %v", err) 59 } 60 } 61 if params.ServiceFilter != "" { 62 if _, err := regexp.Compile(params.ServiceFilter); err != nil { 63 return fmt.Errorf("Invalid service filter: %v", err) 64 } 65 } 66 if params.TagFilter != "" { 67 if _, err := regexp.Compile(params.TagFilter); err != nil { 68 return fmt.Errorf("Invalid tag filter: %v", err) 69 } 70 } 71 return nil 72 } 73 74 // UserEvent is used to fire an event via the Serf layer on the LAN 75 func (a *Agent) UserEvent(dc, token string, params *UserEvent) error { 76 // Validate the params 77 if err := validateUserEventParams(params); err != nil { 78 return err 79 } 80 81 // Format message 82 var err error 83 if params.ID, err = uuid.GenerateUUID(); err != nil { 84 return fmt.Errorf("UUID generation failed: %v", err) 85 } 86 params.Version = userEventMaxVersion 87 payload, err := encodeMsgPack(¶ms) 88 if err != nil { 89 return fmt.Errorf("UserEvent encoding failed: %v", err) 90 } 91 92 // Service the event fire over RPC. This ensures that we authorize 93 // the request against the token first. 94 args := structs.EventFireRequest{ 95 Datacenter: dc, 96 Name: params.Name, 97 Payload: payload, 98 QueryOptions: structs.QueryOptions{Token: token}, 99 } 100 101 // Any server can process in the remote DC, since the 102 // gossip will take over anyways 103 args.AllowStale = true 104 var out structs.EventFireResponse 105 return a.RPC("Internal.EventFire", &args, &out) 106 } 107 108 // handleEvents is used to process incoming user events 109 func (a *Agent) handleEvents() { 110 for { 111 select { 112 case e := <-a.eventCh: 113 // Decode the event 114 msg := new(UserEvent) 115 if err := decodeMsgPack(e.Payload, msg); err != nil { 116 a.logger.Printf("[ERR] agent: Failed to decode event: %v", err) 117 continue 118 } 119 msg.LTime = uint64(e.LTime) 120 121 // Skip if we don't pass filtering 122 if !a.shouldProcessUserEvent(msg) { 123 continue 124 } 125 126 // Ingest the event 127 a.ingestUserEvent(msg) 128 129 case <-a.shutdownCh: 130 return 131 } 132 } 133 } 134 135 // shouldProcessUserEvent checks if an event makes it through our filters 136 func (a *Agent) shouldProcessUserEvent(msg *UserEvent) bool { 137 // Check the version 138 if msg.Version > userEventMaxVersion { 139 a.logger.Printf("[WARN] agent: Event version %d may have unsupported features (%s)", 140 msg.Version, msg.Name) 141 } 142 143 // Apply the filters 144 if msg.NodeFilter != "" { 145 re, err := regexp.Compile(msg.NodeFilter) 146 if err != nil { 147 a.logger.Printf("[ERR] agent: Failed to parse node filter '%s' for event '%s': %v", 148 msg.NodeFilter, msg.Name, err) 149 return false 150 } 151 if !re.MatchString(a.config.NodeName) { 152 return false 153 } 154 } 155 156 if msg.ServiceFilter != "" { 157 re, err := regexp.Compile(msg.ServiceFilter) 158 if err != nil { 159 a.logger.Printf("[ERR] agent: Failed to parse service filter '%s' for event '%s': %v", 160 msg.ServiceFilter, msg.Name, err) 161 return false 162 } 163 164 var tagRe *regexp.Regexp 165 if msg.TagFilter != "" { 166 re, err := regexp.Compile(msg.TagFilter) 167 if err != nil { 168 a.logger.Printf("[ERR] agent: Failed to parse tag filter '%s' for event '%s': %v", 169 msg.TagFilter, msg.Name, err) 170 return false 171 } 172 tagRe = re 173 } 174 175 // Scan for a match 176 services := a.State.Services() 177 found := false 178 OUTER: 179 for name, info := range services { 180 // Check the service name 181 if !re.MatchString(name) { 182 continue 183 } 184 if tagRe == nil { 185 found = true 186 break 187 } 188 189 // Look for a matching tag 190 for _, tag := range info.Tags { 191 if !tagRe.MatchString(tag) { 192 continue 193 } 194 found = true 195 break OUTER 196 } 197 } 198 199 // No matching services 200 if !found { 201 return false 202 } 203 } 204 return true 205 } 206 207 // ingestUserEvent is used to process an event that passes filtering 208 func (a *Agent) ingestUserEvent(msg *UserEvent) { 209 // Special handling for internal events 210 switch msg.Name { 211 case remoteExecName: 212 if a.config.DisableRemoteExec { 213 a.logger.Printf("[INFO] agent: ignoring remote exec event (%s), disabled.", msg.ID) 214 } else { 215 go a.handleRemoteExec(msg) 216 } 217 return 218 default: 219 a.logger.Printf("[DEBUG] agent: new event: %s (%s)", msg.Name, msg.ID) 220 } 221 222 a.eventLock.Lock() 223 defer func() { 224 a.eventLock.Unlock() 225 a.eventNotify.Notify() 226 }() 227 228 idx := a.eventIndex 229 a.eventBuf[idx] = msg 230 a.eventIndex = (idx + 1) % len(a.eventBuf) 231 } 232 233 // UserEvents is used to return a slice of the most recent 234 // user events. 235 func (a *Agent) UserEvents() []*UserEvent { 236 n := len(a.eventBuf) 237 out := make([]*UserEvent, n) 238 a.eventLock.RLock() 239 defer a.eventLock.RUnlock() 240 241 // Check if the buffer is full 242 if a.eventBuf[a.eventIndex] != nil { 243 if a.eventIndex == 0 { 244 copy(out, a.eventBuf) 245 } else { 246 copy(out, a.eventBuf[a.eventIndex:]) 247 copy(out[n-a.eventIndex:], a.eventBuf[:a.eventIndex]) 248 } 249 } else { 250 // We haven't filled the buffer yet 251 copy(out, a.eventBuf[:a.eventIndex]) 252 out = out[:a.eventIndex] 253 } 254 return out 255 } 256 257 // LastUserEvent is used to return the last user event. 258 // This will return nil if there is no recent event. 259 func (a *Agent) LastUserEvent() *UserEvent { 260 a.eventLock.RLock() 261 defer a.eventLock.RUnlock() 262 n := len(a.eventBuf) 263 idx := (((a.eventIndex - 1) % n) + n) % n 264 return a.eventBuf[idx] 265 }