github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/core/presence/presence.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The presence package works on the premise that an agent it alive 5 // if it has a current connection to one of the API servers. 6 // 7 // This package handles all of the logic around collecting an organising 8 // the information around all the connections made to the API servers. 9 package presence 10 11 import ( 12 "sync" 13 "time" 14 15 "github.com/juju/collections/set" 16 "github.com/juju/errors" 17 ) 18 19 // Recorder records the presence status of every apiserver connection 20 // for the agents. 21 type Recorder interface { 22 // Disable clears the entries and marks the recorder as disabled. Note 23 // that all agents will have a recorder, but they are only enabled for 24 // API server agents. 25 Disable() 26 27 // Enable marks the recorder as enabled. 28 Enable() 29 30 // IsEnabled returns whether or not the recorder is enabled. 31 IsEnabled() bool 32 33 // Connect adds an entry for the specified agent. 34 // The server and agent strings are the stringified machine and unit tags. 35 // The model is the UUID for the model. 36 Connect(server, model, agent string, id uint64, controllerAgent bool, userData string) 37 38 // Disconnect removes the entry for the specified connection id. 39 Disconnect(server string, id uint64) 40 41 // Activity updates the last seen timestamp for the connection specified. 42 Activity(server string, id uint64) 43 44 // ServerDown marks all connections on the specified server as unknown. 45 ServerDown(server string) 46 47 // UpdateServer replaces all known connections for the specified server 48 // with the connections specified. If any of the connections are not for 49 // the specified server, an error is returned. 50 UpdateServer(server string, connections []Value) error 51 52 // Connections returns all connections info that the recorder has. 53 Connections() Connections 54 } 55 56 // Connections provides a way to slice the full presence understanding 57 // across various axis like server, model and agent. 58 type Connections interface { 59 // ForModel will return the connections just for the specified model UUID. 60 ForModel(model string) Connections 61 62 // ForServer will return just the connections for agents connected to the specified 63 // server. The server is a stringified machine tag for the API server. 64 ForServer(server string) Connections 65 66 // ForAgent returns the connections for the specified agent in the model. 67 // The agent is the stringified machine or unit tag. 68 ForAgent(agent string) Connections 69 70 // Count returns the number of connections. 71 Count() int 72 73 // Models returns all the model UUIDs that have connections. 74 Models() []string 75 76 // Servers returns all the API servers that have connections in this 77 // collection. 78 Servers() []string 79 80 // Agents returns all the stringified agent tags that have connections 81 // in this collection. 82 Agents() []string 83 84 // For a given non controller agent, return the Status for that agent. 85 AgentStatus(agent string) (Status, error) 86 87 // Values returns the connection information for this collection. 88 Values() []Value 89 } 90 91 // Status represents the state of a given agent's presence. 92 type Status int 93 94 const ( 95 // Unknown means that the agent specified is not in the collection. 96 Unknown Status = iota 97 98 // Missing means that the agent was connected, but the server that it was 99 // connected to has gone away and not yet come back. 100 Missing 101 102 // Alive means that the connection is active. 103 Alive 104 ) 105 106 // String implements Stringer. 107 func (s Status) String() string { 108 switch s { 109 case Unknown: 110 return "unknown" 111 case Missing: 112 return "missing" 113 case Alive: 114 return "alive" 115 default: 116 return "" 117 } 118 } 119 120 // Value holds the information about a single agent connection to an apiserver 121 // machine. 122 type Value struct { 123 // Model is the model UUID. 124 Model string 125 126 // Server is the stringified machine tag of the API server. 127 Server string 128 129 // Agent is the stringified machine, unit, or application tag of the agent. 130 Agent string 131 132 // ControllerAgent is true if the agent is in the controller model. 133 ControllerAgent bool 134 135 // ConnectionID is the unique identifier given by the API server. 136 ConnectionID uint64 137 138 // Status is either Missing or Alive. 139 Status Status 140 141 // UserData is the user data provided with the Login API call. 142 UserData string 143 144 // LastSeen is the timestamp when the connection was added using 145 // Connect, or the last time Activity was called. 146 LastSeen time.Time 147 } 148 149 type connections struct { 150 model string 151 values []Value 152 } 153 154 // Clock provides an interface for dealing with clocks. 155 type Clock interface { 156 // Now returns the current clock time. 157 Now() time.Time 158 } 159 160 // New returns a new empty Recorder. 161 func New(clock Clock) Recorder { 162 return &recorder{clock: clock} 163 } 164 165 type recorder struct { 166 mu sync.Mutex 167 enabled bool 168 clock Clock 169 entries []Value 170 } 171 172 // Disable implements Recorder. 173 func (r *recorder) Disable() { 174 r.mu.Lock() 175 defer r.mu.Unlock() 176 r.enabled = false 177 r.entries = nil 178 } 179 180 // Enable implements Recorder. 181 func (r *recorder) Enable() { 182 r.mu.Lock() 183 defer r.mu.Unlock() 184 r.enabled = true 185 186 } 187 188 // IsEnabled implements Recorder. 189 func (r *recorder) IsEnabled() bool { 190 r.mu.Lock() 191 defer r.mu.Unlock() 192 return r.enabled 193 } 194 195 func (r *recorder) findIndex(server string, id uint64) int { 196 for i, e := range r.entries { 197 if e.Server == server && e.ConnectionID == id { 198 return i 199 } 200 } 201 return -1 202 } 203 204 // Connect implements Recorder. 205 func (r *recorder) Connect(server, model, agent string, id uint64, controllerAgent bool, userData string) { 206 r.mu.Lock() 207 defer r.mu.Unlock() 208 if !r.enabled { 209 return 210 } 211 212 pos := r.findIndex(server, id) 213 if pos == -1 { 214 r.entries = append(r.entries, Value{ 215 Model: model, 216 Server: server, 217 Agent: agent, 218 ControllerAgent: controllerAgent, 219 ConnectionID: id, 220 Status: Alive, 221 UserData: userData, 222 LastSeen: r.clock.Now(), 223 }) 224 } else { 225 // Need to access the value in the array, not a copy of it. 226 r.entries[pos].Status = Alive 227 r.entries[pos].LastSeen = r.clock.Now() 228 } 229 } 230 231 // Disconnect implements Recorder. 232 func (r *recorder) Disconnect(server string, id uint64) { 233 r.mu.Lock() 234 defer r.mu.Unlock() 235 if !r.enabled { 236 return 237 } 238 239 if pos := r.findIndex(server, id); pos >= 0 { 240 if pos == 0 { 241 r.entries = r.entries[1:] 242 } else { 243 r.entries = append(r.entries[0:pos], r.entries[pos+1:]...) 244 } 245 } 246 } 247 248 // Activity implements Recorder. 249 func (r *recorder) Activity(server string, id uint64) { 250 r.mu.Lock() 251 defer r.mu.Unlock() 252 if !r.enabled { 253 return 254 } 255 256 if pos := r.findIndex(server, id); pos >= 0 { 257 r.entries[pos].LastSeen = r.clock.Now() 258 } 259 } 260 261 // ServerDown implements Recorder. 262 func (r *recorder) ServerDown(server string) { 263 r.mu.Lock() 264 defer r.mu.Unlock() 265 if !r.enabled { 266 return 267 } 268 269 for i, value := range r.entries { 270 if value.Server == server { 271 r.entries[i].Status = Missing 272 } 273 } 274 } 275 276 // UpdateServer implements Recorder. 277 func (r *recorder) UpdateServer(server string, connections []Value) error { 278 r.mu.Lock() 279 defer r.mu.Unlock() 280 if !r.enabled { 281 return errors.New("recorder not enabled") 282 } 283 284 entries := make([]Value, 0, len(r.entries)) 285 for _, value := range r.entries { 286 if value.Server != server { 287 entries = append(entries, value) 288 } 289 } 290 291 for _, value := range connections { 292 if value.Server != server { 293 return errors.Errorf("connection server mismatch, got %q expected %q", value.Server, server) 294 } 295 value.Status = Alive 296 value.LastSeen = r.clock.Now() 297 entries = append(entries, value) 298 } 299 300 r.entries = entries 301 return nil 302 } 303 304 // Connections implements Recorder. 305 func (r *recorder) Connections() Connections { 306 r.mu.Lock() 307 defer r.mu.Unlock() 308 309 entries := make([]Value, len(r.entries)) 310 copy(entries, r.entries) 311 return &connections{values: entries} 312 } 313 314 // ForModel implements Connections. 315 func (c *connections) ForModel(model string) Connections { 316 var values []Value 317 for _, value := range c.values { 318 if value.Model == model { 319 values = append(values, value) 320 } 321 } 322 return &connections{model: model, values: values} 323 } 324 325 // ForServer implements Connections. 326 func (c *connections) ForServer(server string) Connections { 327 var values []Value 328 for _, value := range c.values { 329 if value.Server == server { 330 values = append(values, value) 331 } 332 } 333 return &connections{model: c.model, values: values} 334 } 335 336 // ForAgent implements Connections. 337 func (c *connections) ForAgent(agent string) Connections { 338 var values []Value 339 for _, value := range c.values { 340 if value.Agent == agent { 341 values = append(values, value) 342 } 343 } 344 return &connections{model: c.model, values: values} 345 } 346 347 // Count implements Connections. 348 func (c *connections) Count() int { 349 return len(c.values) 350 } 351 352 // Models implements Connections. 353 func (c *connections) Models() []string { 354 models := set.NewStrings() 355 for _, value := range c.values { 356 models.Add(value.Model) 357 } 358 return models.Values() 359 } 360 361 // Servers implements Connections. 362 func (c *connections) Servers() []string { 363 servers := set.NewStrings() 364 for _, value := range c.values { 365 servers.Add(value.Server) 366 } 367 return servers.Values() 368 } 369 370 // Agents implements Connections. 371 func (c *connections) Agents() []string { 372 agents := set.NewStrings() 373 for _, value := range c.values { 374 agents.Add(value.Agent) 375 } 376 return agents.Values() 377 } 378 379 // AgentStatus implements Connections. 380 func (c *connections) AgentStatus(agent string) (Status, error) { 381 if c.model == "" { 382 return Unknown, errors.New("connections not limited to a model, agent ambiguous") 383 } 384 result := Unknown 385 for _, value := range c.values { 386 if value.Agent == agent && !value.ControllerAgent { 387 if value.Status > result { 388 result = value.Status 389 } 390 } 391 } 392 return result, nil 393 } 394 395 // Values implements Connections. 396 func (c *connections) Values() []Value { 397 return c.values 398 }