github.com/grafana/synthetic-monitoring-agent@v0.64.2/cmd/test-api/internal/db/db.go (about) 1 package db 2 3 import ( 4 "context" 5 "errors" 6 "slices" 7 "sync" 8 "time" 9 10 sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring" 11 ) 12 13 type Db struct { 14 mutex sync.Mutex 15 tenants map[int64]sm.Tenant 16 probes map[int64]sm.Probe 17 probeTokens map[string]int64 18 checks map[int64]sm.Check 19 checksByProbe map[int64][]int64 20 } 21 22 func New() *Db { 23 return &Db{ 24 tenants: make(map[int64]sm.Tenant), 25 probes: make(map[int64]sm.Probe), 26 probeTokens: make(map[string]int64), 27 checks: make(map[int64]sm.Check), 28 checksByProbe: make(map[int64][]int64), 29 } 30 } 31 32 func (db *Db) FindProbeIDByToken(ctx context.Context, token []byte) (int64, error) { 33 db.mutex.Lock() 34 defer db.mutex.Unlock() 35 36 if id, found := db.probeTokens[string(token)]; found { 37 return id, nil 38 } 39 40 return -1, errors.New("token not found") 41 } 42 43 func (db *Db) FindProbeByID(ctx context.Context, id int64) (*sm.Probe, error) { 44 db.mutex.Lock() 45 defer db.mutex.Unlock() 46 47 if probe, found := db.probes[id]; found { 48 return &probe, nil 49 } 50 51 return nil, errors.New("probe not found") 52 } 53 54 func (db *Db) ListChecksForProbe(ctx context.Context, id int64) ([]sm.Check, error) { 55 db.mutex.Lock() 56 defer db.mutex.Unlock() 57 58 checks := make([]sm.Check, 0, len(db.checksByProbe[id])) 59 60 for _, checkId := range db.checksByProbe[id] { 61 checks = append(checks, db.checks[checkId]) 62 } 63 64 return checks, nil 65 } 66 67 func (db *Db) AddTenant(ctx context.Context, tenant *sm.Tenant) error { 68 db.mutex.Lock() 69 defer db.mutex.Unlock() 70 71 for _, t := range db.tenants { 72 if t.StackId == tenant.StackId { 73 return errors.New("tenant already exists") 74 } 75 } 76 77 now := nsNow() 78 79 newTenant := sm.Tenant{ 80 Id: int64(len(db.tenants) + 1), 81 OrgId: tenant.OrgId, 82 StackId: tenant.StackId, 83 Status: sm.TenantStatus_ACTIVE, 84 Reason: "", 85 Created: now, 86 Modified: now, 87 } 88 if tenant.MetricsRemote != nil { 89 newTenant.MetricsRemote = &sm.RemoteInfo{ 90 Name: tenant.MetricsRemote.Name, 91 Url: tenant.MetricsRemote.Url, 92 Username: tenant.MetricsRemote.Username, 93 Password: tenant.MetricsRemote.Password, 94 } 95 } 96 97 if tenant.EventsRemote != nil { 98 newTenant.EventsRemote = &sm.RemoteInfo{ 99 Name: tenant.EventsRemote.Name, 100 Url: tenant.EventsRemote.Url, 101 Username: tenant.EventsRemote.Username, 102 Password: tenant.EventsRemote.Password, 103 } 104 } 105 106 db.tenants[newTenant.Id] = newTenant 107 108 *tenant = newTenant 109 110 return nil 111 } 112 113 func (db *Db) GetTenant(ctx context.Context, id int64) (*sm.Tenant, error) { 114 db.mutex.Lock() 115 defer db.mutex.Unlock() 116 117 tenant, found := db.tenants[id] 118 if !found { 119 return nil, errors.New("tenant not found") 120 } 121 122 return &tenant, nil 123 } 124 125 func (db *Db) ListTenants(ctx context.Context) ([]sm.Tenant, error) { 126 db.mutex.Lock() 127 defer db.mutex.Unlock() 128 129 tenants := make([]sm.Tenant, 0, len(db.tenants)) 130 for _, tenant := range db.tenants { 131 tenants = append(tenants, tenant) 132 } 133 134 return tenants, nil 135 } 136 137 func (db *Db) UpdateTenant(ctx context.Context, tenant *sm.Tenant) (*sm.Tenant, error) { 138 db.mutex.Lock() 139 defer db.mutex.Unlock() 140 141 if _, found := db.tenants[tenant.Id]; !found { 142 return nil, errors.New("tenant not found") 143 } 144 145 tenant.Modified = nsNow() 146 db.tenants[tenant.Id] = *tenant 147 148 return tenant, nil 149 } 150 151 func (db *Db) DeleteTenant(ctx context.Context, id int64) error { 152 db.mutex.Lock() 153 defer db.mutex.Unlock() 154 155 if _, found := db.tenants[id]; !found { 156 return errors.New("tenant not found") 157 } 158 159 for _, probe := range db.probes { 160 if probe.TenantId == id { 161 return errors.New("tenant is still referenced by probe") 162 } 163 } 164 165 for _, check := range db.checks { 166 if check.TenantId == id { 167 return errors.New("tenant is still referenced by check") 168 } 169 } 170 171 delete(db.tenants, id) 172 173 return nil 174 } 175 176 func (db *Db) AddProbe(ctx context.Context, probe *sm.Probe, token []byte) error { 177 db.mutex.Lock() 178 defer db.mutex.Unlock() 179 180 if err := probe.Validate(); err != nil { 181 return err 182 } 183 184 if _, found := db.tenants[probe.TenantId]; !found { 185 return errors.New("invalid tenant") 186 } 187 188 for _, p := range db.probes { 189 // if the new probe is public, it cannot use an existing name in any of the probes 190 // if the existing probe is public, the new probe cannot use its name 191 // if they are private and both are in the same tenant, they cannot have the same name 192 if (probe.Public || p.TenantId == probe.TenantId || p.Public) && p.Name == probe.Name { 193 return errors.New("probe already exists") 194 } 195 } 196 197 if _, found := db.probeTokens[string(token)]; found { 198 return errors.New("invalid probe token") 199 } 200 201 probe.Id = int64(len(db.probes) + 1) 202 probe.Created = nsNow() 203 probe.Modified = probe.Created 204 205 db.probes[probe.Id] = *probe 206 db.probeTokens[string(token)] = probe.Id 207 208 return nil 209 } 210 211 func (db *Db) GetProbe(ctx context.Context, id int64) (*sm.Probe, error) { 212 db.mutex.Lock() 213 defer db.mutex.Unlock() 214 215 if probe, found := db.probes[id]; found { 216 return &probe, nil 217 } 218 219 return nil, errors.New("probe not found") 220 } 221 222 func (db *Db) ListProbes(ctx context.Context) ([]sm.Probe, error) { 223 db.mutex.Lock() 224 defer db.mutex.Unlock() 225 226 probes := make([]sm.Probe, 0, len(db.probes)) 227 228 for _, probe := range db.probes { 229 probes = append(probes, probe) 230 } 231 232 return probes, nil 233 } 234 235 func (db *Db) UpdateProbe(ctx context.Context, probe *sm.Probe) (*sm.Probe, error) { 236 db.mutex.Lock() 237 defer db.mutex.Unlock() 238 239 oldProbe, found := db.probes[probe.Id] 240 if !found { 241 return nil, errors.New("probe not found") 242 } 243 244 if oldProbe.Name != probe.Name { 245 return nil, errors.New("cannot rename probes") 246 } 247 248 if oldProbe.TenantId != probe.TenantId { 249 return nil, errors.New("cannot move probes between tenants") 250 } 251 252 if err := probe.Validate(); err != nil { 253 return nil, err 254 } 255 256 for _, p := range db.probes { 257 if p.Id == probe.Id { 258 continue 259 } 260 261 // If the upated probe is public (or becoming public), it 262 // cannot share the name with any other probe. 263 if probe.Public && p.Name == probe.Name { 264 return nil, errors.New("invalid probe name") 265 } 266 } 267 268 updatedProbe := *probe 269 updatedProbe.Modified = nsNow() 270 271 db.probes[probe.Id] = updatedProbe 272 273 return &updatedProbe, nil 274 } 275 276 func (db *Db) DeleteProbe(ctx context.Context, id int64) error { 277 db.mutex.Lock() 278 defer db.mutex.Unlock() 279 280 if _, found := db.probes[id]; !found { 281 return errors.New("probe not found") 282 } 283 284 for _, check := range db.checks { 285 if slices.Contains(check.Probes, id) { 286 return errors.New("probe is still referenced by check") 287 } 288 } 289 290 for token, probeId := range db.probeTokens { 291 if probeId == id { 292 delete(db.probeTokens, token) 293 } 294 } 295 296 delete(db.probes, id) 297 298 return nil 299 } 300 301 func (db *Db) AddCheck(ctx context.Context, check *sm.Check) error { 302 db.mutex.Lock() 303 defer db.mutex.Unlock() 304 305 if err := check.Validate(); err != nil { 306 return err 307 } 308 309 if _, found := db.tenants[check.TenantId]; !found { 310 return errors.New("invalid tenant") 311 } 312 313 for _, c := range db.checks { 314 if c.Target == check.Target && c.Job == check.Job { 315 return errors.New("check already exists") 316 } 317 } 318 319 for _, pId := range check.Probes { 320 probe, found := db.probes[pId] 321 if !found { 322 return errors.New("probe not found") 323 } 324 325 if probe.TenantId != check.TenantId && !probe.Public { 326 return errors.New("invalid probe") 327 } 328 } 329 330 check.Id = int64(len(db.checks) + 1) 331 check.Created = nsNow() 332 check.Modified = check.Created 333 334 db.checks[check.Id] = *check 335 336 // now that we now everything is valid, add checks to probes 337 for _, pId := range check.Probes { 338 db.checksByProbe[pId] = append(db.checksByProbe[pId], check.Id) 339 } 340 341 return nil 342 } 343 344 func (db *Db) GetCheck(ctx context.Context, id int64) (*sm.Check, error) { 345 db.mutex.Lock() 346 defer db.mutex.Unlock() 347 348 if check, found := db.checks[id]; found { 349 return &check, nil 350 } 351 352 return nil, errors.New("check not found") 353 } 354 355 func (db *Db) ListChecks(ctx context.Context) ([]sm.Check, error) { 356 db.mutex.Lock() 357 defer db.mutex.Unlock() 358 359 checks := make([]sm.Check, 0, len(db.checks)) 360 361 for _, check := range db.checks { 362 checks = append(checks, check) 363 } 364 365 return checks, nil 366 } 367 368 func (db *Db) UpdateCheck(ctx context.Context, check *sm.Check) (*sm.Check, error) { 369 db.mutex.Lock() 370 defer db.mutex.Unlock() 371 372 oldCheck, found := db.checks[check.Id] 373 if !found { 374 return nil, errors.New("check not found") 375 } 376 377 if oldCheck.Job != check.Job { 378 return nil, errors.New("cannot change job") 379 } 380 381 if oldCheck.Target != check.Target { 382 return nil, errors.New("cannot change target") 383 } 384 385 if oldCheck.TenantId != check.TenantId { 386 return nil, errors.New("cannot move checks between tenants") 387 } 388 389 if err := check.Validate(); err != nil { 390 return nil, err 391 } 392 393 check.Modified = nsNow() 394 395 db.checks[check.Id] = *check 396 397 return &oldCheck, nil 398 } 399 400 func (db *Db) DeleteCheck(ctx context.Context, id int64) error { 401 db.mutex.Lock() 402 defer db.mutex.Unlock() 403 404 if _, found := db.checks[id]; !found { 405 return errors.New("check not found") 406 } 407 408 delete(db.checks, id) 409 410 return nil 411 } 412 413 func nsNow() float64 { 414 return float64(time.Now().UnixNano()) / 1e9 415 }