github.com/scaleway/scaleway-cli@v1.11.1/pkg/api/cache.go (about) 1 // Copyright (C) 2015 Scaleway. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE.md file. 4 5 package api 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "regexp" 14 "strings" 15 "sync" 16 17 "github.com/moul/anonuuid" 18 "github.com/renstrom/fuzzysearch/fuzzy" 19 ) 20 21 const ( 22 // CacheRegion permits to access at the region field 23 CacheRegion = iota 24 // CacheArch permits to access at the arch field 25 CacheArch 26 // CacheOwner permits to access at the owner field 27 CacheOwner 28 // CacheTitle permits to access at the title field 29 CacheTitle 30 // CacheMarketPlaceUUID is used to determine the UUID of local images 31 CacheMarketPlaceUUID 32 // CacheMaxfield is used to determine the size of array 33 CacheMaxfield 34 ) 35 36 // ScalewayCache is used not to query the API to resolve full identifiers 37 type ScalewayCache struct { 38 // Images contains names of Scaleway images indexed by identifier 39 Images map[string][CacheMaxfield]string `json:"images"` 40 41 // Snapshots contains names of Scaleway snapshots indexed by identifier 42 Snapshots map[string][CacheMaxfield]string `json:"snapshots"` 43 44 // Volumes contains names of Scaleway volumes indexed by identifier 45 Volumes map[string][CacheMaxfield]string `json:"volumes"` 46 47 // Bootscripts contains names of Scaleway bootscripts indexed by identifier 48 Bootscripts map[string][CacheMaxfield]string `json:"bootscripts"` 49 50 // Servers contains names of Scaleway servers indexed by identifier 51 Servers map[string][CacheMaxfield]string `json:"servers"` 52 53 // Path is the path to the cache file 54 Path string `json:"-"` 55 56 // Modified tells if the cache needs to be overwritten or not 57 Modified bool `json:"-"` 58 59 // Lock allows ScalewayCache to be used concurrently 60 Lock sync.Mutex `json:"-"` 61 62 hookSave func() 63 } 64 65 const ( 66 // IdentifierUnknown is used when we don't know explicitly the type key of the object (used for nil comparison) 67 IdentifierUnknown = 1 << iota 68 // IdentifierServer is the type key of cached server objects 69 IdentifierServer 70 // IdentifierImage is the type key of cached image objects 71 IdentifierImage 72 // IdentifierSnapshot is the type key of cached snapshot objects 73 IdentifierSnapshot 74 // IdentifierBootscript is the type key of cached bootscript objects 75 IdentifierBootscript 76 // IdentifierVolume is the type key of cached volume objects 77 IdentifierVolume 78 ) 79 80 // ScalewayResolverResult is a structure containing human-readable information 81 // about resolver results. This structure is used to display the user choices. 82 type ScalewayResolverResult struct { 83 Identifier string 84 Type int 85 Name string 86 Arch string 87 Needle string 88 RankMatch int 89 Region string 90 } 91 92 // ScalewayResolverResults is a list of `ScalewayResolverResult` 93 type ScalewayResolverResults []ScalewayResolverResult 94 95 // NewScalewayResolverResult returns a new ScalewayResolverResult 96 func NewScalewayResolverResult(Identifier, Name, Arch, Region string, Type int) (ScalewayResolverResult, error) { 97 if err := anonuuid.IsUUID(Identifier); err != nil { 98 return ScalewayResolverResult{}, err 99 } 100 return ScalewayResolverResult{ 101 Identifier: Identifier, 102 Type: Type, 103 Name: Name, 104 Arch: Arch, 105 Region: Region, 106 }, nil 107 } 108 109 func (s ScalewayResolverResults) Len() int { 110 return len(s) 111 } 112 113 func (s ScalewayResolverResults) Swap(i, j int) { 114 s[i], s[j] = s[j], s[i] 115 } 116 117 func (s ScalewayResolverResults) Less(i, j int) bool { 118 return s[i].RankMatch < s[j].RankMatch 119 } 120 121 // TruncIdentifier returns first 8 characters of an Identifier (UUID) 122 func (s *ScalewayResolverResult) TruncIdentifier() string { 123 return s.Identifier[:8] 124 } 125 126 func identifierTypeName(kind int) string { 127 switch kind { 128 case IdentifierServer: 129 return "Server" 130 case IdentifierImage: 131 return "Image" 132 case IdentifierSnapshot: 133 return "Snapshot" 134 case IdentifierVolume: 135 return "Volume" 136 case IdentifierBootscript: 137 return "Bootscript" 138 } 139 return "" 140 } 141 142 // CodeName returns a full resource name with typed prefix 143 func (s *ScalewayResolverResult) CodeName() string { 144 name := strings.ToLower(s.Name) 145 name = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(name, "-") 146 name = regexp.MustCompile(`--+`).ReplaceAllString(name, "-") 147 name = strings.Trim(name, "-") 148 149 return fmt.Sprintf("%s:%s", strings.ToLower(identifierTypeName(s.Type)), name) 150 } 151 152 // FilterByArch deletes the elements which not match with arch 153 func (s *ScalewayResolverResults) FilterByArch(arch string) { 154 REDO: 155 for i := range *s { 156 if (*s)[i].Arch != arch { 157 (*s)[i] = (*s)[len(*s)-1] 158 *s = (*s)[:len(*s)-1] 159 goto REDO 160 } 161 } 162 } 163 164 // NewScalewayCache loads a per-user cache 165 func NewScalewayCache(hookSave func()) (*ScalewayCache, error) { 166 var cache ScalewayCache 167 168 cache.hookSave = hookSave 169 homeDir := os.Getenv("HOME") // *nix 170 if homeDir == "" { // Windows 171 homeDir = os.Getenv("USERPROFILE") 172 } 173 if homeDir == "" { 174 homeDir = "/tmp" 175 } 176 cachePath := filepath.Join(homeDir, ".scw-cache.db") 177 cache.Path = cachePath 178 _, err := os.Stat(cachePath) 179 if os.IsNotExist(err) { 180 cache.Clear() 181 return &cache, nil 182 } else if err != nil { 183 return nil, err 184 } 185 file, err := ioutil.ReadFile(cachePath) 186 if err != nil { 187 return nil, err 188 } 189 err = json.Unmarshal(file, &cache) 190 if err != nil { 191 // fix compatibility with older version 192 if err = os.Remove(cachePath); err != nil { 193 return nil, err 194 } 195 cache.Clear() 196 return &cache, nil 197 } 198 if cache.Images == nil { 199 cache.Images = make(map[string][CacheMaxfield]string) 200 } 201 if cache.Snapshots == nil { 202 cache.Snapshots = make(map[string][CacheMaxfield]string) 203 } 204 if cache.Volumes == nil { 205 cache.Volumes = make(map[string][CacheMaxfield]string) 206 } 207 if cache.Servers == nil { 208 cache.Servers = make(map[string][CacheMaxfield]string) 209 } 210 if cache.Bootscripts == nil { 211 cache.Bootscripts = make(map[string][CacheMaxfield]string) 212 } 213 return &cache, nil 214 } 215 216 // Clear removes all information from the cache 217 func (c *ScalewayCache) Clear() { 218 c.Images = make(map[string][CacheMaxfield]string) 219 c.Snapshots = make(map[string][CacheMaxfield]string) 220 c.Volumes = make(map[string][CacheMaxfield]string) 221 c.Bootscripts = make(map[string][CacheMaxfield]string) 222 c.Servers = make(map[string][CacheMaxfield]string) 223 c.Modified = true 224 } 225 226 // Flush flushes the cache database 227 func (c *ScalewayCache) Flush() error { 228 return os.Remove(c.Path) 229 } 230 231 // Save atomically overwrites the current cache database 232 func (c *ScalewayCache) Save() error { 233 c.Lock.Lock() 234 defer c.Lock.Unlock() 235 236 c.hookSave() 237 if c.Modified { 238 file, err := ioutil.TempFile(filepath.Dir(c.Path), filepath.Base(c.Path)) 239 if err != nil { 240 return err 241 } 242 defer file.Close() 243 if err := json.NewEncoder(file).Encode(c); err != nil { 244 os.Remove(file.Name()) 245 return err 246 } 247 248 if err := os.Rename(file.Name(), c.Path); err != nil { 249 os.Remove(file.Name()) 250 return err 251 } 252 } 253 return nil 254 } 255 256 // ComputeRankMatch fills `ScalewayResolverResult.RankMatch` with its `fuzzy` score 257 func (s *ScalewayResolverResult) ComputeRankMatch(needle string) { 258 s.Needle = needle 259 s.RankMatch = fuzzy.RankMatch(needle, s.Name) 260 } 261 262 // LookUpImages attempts to return identifiers matching a pattern 263 func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (ScalewayResolverResults, error) { 264 c.Lock.Lock() 265 defer c.Lock.Unlock() 266 267 var res ScalewayResolverResults 268 var exactMatches ScalewayResolverResults 269 270 if acceptUUID && anonuuid.IsUUID(needle) == nil { 271 if fields, ok := c.Images[needle]; ok { 272 entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) 273 if err != nil { 274 return ScalewayResolverResults{}, err 275 } 276 entry.ComputeRankMatch(needle) 277 res = append(res, entry) 278 } 279 } 280 281 needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") 282 // FIXME: if 'user/' is in needle, only watch for a user image 283 nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) 284 for identifier, fields := range c.Images { 285 if fields[CacheTitle] == needle { 286 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) 287 if err != nil { 288 return ScalewayResolverResults{}, err 289 } 290 entry.ComputeRankMatch(needle) 291 exactMatches = append(exactMatches, entry) 292 } 293 if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { 294 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) 295 if err != nil { 296 return ScalewayResolverResults{}, err 297 } 298 entry.ComputeRankMatch(needle) 299 res = append(res, entry) 300 } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { 301 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierImage) 302 if err != nil { 303 return ScalewayResolverResults{}, err 304 } 305 entry.ComputeRankMatch(needle) 306 res = append(res, entry) 307 } 308 } 309 310 if len(exactMatches) == 1 { 311 return exactMatches, nil 312 } 313 314 return removeDuplicatesResults(res), nil 315 } 316 317 // LookUpSnapshots attempts to return identifiers matching a pattern 318 func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) (ScalewayResolverResults, error) { 319 c.Lock.Lock() 320 defer c.Lock.Unlock() 321 322 var res ScalewayResolverResults 323 var exactMatches ScalewayResolverResults 324 325 if acceptUUID && anonuuid.IsUUID(needle) == nil { 326 if fields, ok := c.Snapshots[needle]; ok { 327 entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) 328 if err != nil { 329 return ScalewayResolverResults{}, err 330 } 331 entry.ComputeRankMatch(needle) 332 res = append(res, entry) 333 } 334 } 335 336 needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") 337 nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) 338 for identifier, fields := range c.Snapshots { 339 if fields[CacheTitle] == needle { 340 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) 341 if err != nil { 342 return ScalewayResolverResults{}, err 343 } 344 entry.ComputeRankMatch(needle) 345 exactMatches = append(exactMatches, entry) 346 } 347 if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { 348 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierSnapshot) 349 if err != nil { 350 return ScalewayResolverResults{}, err 351 } 352 entry.ComputeRankMatch(needle) 353 res = append(res, entry) 354 } 355 } 356 357 if len(exactMatches) == 1 { 358 return exactMatches, nil 359 } 360 361 return removeDuplicatesResults(res), nil 362 } 363 364 // LookUpVolumes attempts to return identifiers matching a pattern 365 func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) (ScalewayResolverResults, error) { 366 c.Lock.Lock() 367 defer c.Lock.Unlock() 368 369 var res ScalewayResolverResults 370 var exactMatches ScalewayResolverResults 371 372 if acceptUUID && anonuuid.IsUUID(needle) == nil { 373 if fields, ok := c.Volumes[needle]; ok { 374 entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) 375 if err != nil { 376 return ScalewayResolverResults{}, err 377 } 378 entry.ComputeRankMatch(needle) 379 res = append(res, entry) 380 } 381 } 382 383 nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) 384 for identifier, fields := range c.Volumes { 385 if fields[CacheTitle] == needle { 386 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) 387 if err != nil { 388 return ScalewayResolverResults{}, err 389 } 390 entry.ComputeRankMatch(needle) 391 exactMatches = append(exactMatches, entry) 392 } 393 if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { 394 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierVolume) 395 if err != nil { 396 return ScalewayResolverResults{}, err 397 } 398 entry.ComputeRankMatch(needle) 399 res = append(res, entry) 400 } 401 } 402 403 if len(exactMatches) == 1 { 404 return exactMatches, nil 405 } 406 407 return removeDuplicatesResults(res), nil 408 } 409 410 // LookUpBootscripts attempts to return identifiers matching a pattern 411 func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) (ScalewayResolverResults, error) { 412 c.Lock.Lock() 413 defer c.Lock.Unlock() 414 415 var res ScalewayResolverResults 416 var exactMatches ScalewayResolverResults 417 418 if acceptUUID && anonuuid.IsUUID(needle) == nil { 419 if fields, ok := c.Bootscripts[needle]; ok { 420 entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) 421 if err != nil { 422 return ScalewayResolverResults{}, err 423 } 424 entry.ComputeRankMatch(needle) 425 res = append(res, entry) 426 } 427 } 428 429 nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) 430 for identifier, fields := range c.Bootscripts { 431 if fields[CacheTitle] == needle { 432 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) 433 if err != nil { 434 return ScalewayResolverResults{}, err 435 } 436 entry.ComputeRankMatch(needle) 437 exactMatches = append(exactMatches, entry) 438 } 439 if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { 440 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierBootscript) 441 if err != nil { 442 return ScalewayResolverResults{}, err 443 } 444 entry.ComputeRankMatch(needle) 445 res = append(res, entry) 446 } 447 } 448 449 if len(exactMatches) == 1 { 450 return exactMatches, nil 451 } 452 453 return removeDuplicatesResults(res), nil 454 } 455 456 // LookUpServers attempts to return identifiers matching a pattern 457 func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) (ScalewayResolverResults, error) { 458 c.Lock.Lock() 459 defer c.Lock.Unlock() 460 461 var res ScalewayResolverResults 462 var exactMatches ScalewayResolverResults 463 464 if acceptUUID && anonuuid.IsUUID(needle) == nil { 465 if fields, ok := c.Servers[needle]; ok { 466 entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) 467 if err != nil { 468 return ScalewayResolverResults{}, err 469 } 470 entry.ComputeRankMatch(needle) 471 res = append(res, entry) 472 } 473 } 474 475 nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) 476 for identifier, fields := range c.Servers { 477 if fields[CacheTitle] == needle { 478 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) 479 if err != nil { 480 return ScalewayResolverResults{}, err 481 } 482 entry.ComputeRankMatch(needle) 483 exactMatches = append(exactMatches, entry) 484 } 485 if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { 486 entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], IdentifierServer) 487 if err != nil { 488 return ScalewayResolverResults{}, err 489 } 490 entry.ComputeRankMatch(needle) 491 res = append(res, entry) 492 } 493 } 494 495 if len(exactMatches) == 1 { 496 return exactMatches, nil 497 } 498 499 return removeDuplicatesResults(res), nil 500 } 501 502 // removeDuplicatesResults transforms an array into a unique array 503 func removeDuplicatesResults(elements ScalewayResolverResults) ScalewayResolverResults { 504 encountered := map[string]ScalewayResolverResult{} 505 506 // Create a map of all unique elements. 507 for v := range elements { 508 encountered[elements[v].Identifier] = elements[v] 509 } 510 511 // Place all keys from the map into a slice. 512 results := ScalewayResolverResults{} 513 for _, result := range encountered { 514 results = append(results, result) 515 } 516 return results 517 } 518 519 // parseNeedle parses a user needle and try to extract a forced object type 520 // i.e: 521 // - server:blah-blah -> kind=server, needle=blah-blah 522 // - blah-blah -> kind="", needle=blah-blah 523 // - not-existing-type:blah-blah 524 func parseNeedle(input string) (identifierType int, needle string) { 525 parts := strings.Split(input, ":") 526 if len(parts) == 2 { 527 switch parts[0] { 528 case "server": 529 return IdentifierServer, parts[1] 530 case "image": 531 return IdentifierImage, parts[1] 532 case "snapshot": 533 return IdentifierSnapshot, parts[1] 534 case "bootscript": 535 return IdentifierBootscript, parts[1] 536 case "volume": 537 return IdentifierVolume, parts[1] 538 } 539 } 540 return IdentifierUnknown, input 541 } 542 543 // LookUpIdentifiers attempts to return identifiers matching a pattern 544 func (c *ScalewayCache) LookUpIdentifiers(needle string) (ScalewayResolverResults, error) { 545 results := ScalewayResolverResults{} 546 547 identifierType, needle := parseNeedle(needle) 548 549 if identifierType&(IdentifierUnknown|IdentifierServer) > 0 { 550 servers, err := c.LookUpServers(needle, false) 551 if err != nil { 552 return ScalewayResolverResults{}, err 553 } 554 for _, result := range servers { 555 entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierServer) 556 if err != nil { 557 return ScalewayResolverResults{}, err 558 } 559 entry.ComputeRankMatch(needle) 560 results = append(results, entry) 561 } 562 } 563 564 if identifierType&(IdentifierUnknown|IdentifierImage) > 0 { 565 images, err := c.LookUpImages(needle, false) 566 if err != nil { 567 return ScalewayResolverResults{}, err 568 } 569 for _, result := range images { 570 entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierImage) 571 if err != nil { 572 return ScalewayResolverResults{}, err 573 } 574 entry.ComputeRankMatch(needle) 575 results = append(results, entry) 576 } 577 } 578 579 if identifierType&(IdentifierUnknown|IdentifierSnapshot) > 0 { 580 snapshots, err := c.LookUpSnapshots(needle, false) 581 if err != nil { 582 return ScalewayResolverResults{}, err 583 } 584 for _, result := range snapshots { 585 entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierSnapshot) 586 if err != nil { 587 return ScalewayResolverResults{}, err 588 } 589 entry.ComputeRankMatch(needle) 590 results = append(results, entry) 591 } 592 } 593 594 if identifierType&(IdentifierUnknown|IdentifierVolume) > 0 { 595 volumes, err := c.LookUpVolumes(needle, false) 596 if err != nil { 597 return ScalewayResolverResults{}, err 598 } 599 for _, result := range volumes { 600 entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierVolume) 601 if err != nil { 602 return ScalewayResolverResults{}, err 603 } 604 entry.ComputeRankMatch(needle) 605 results = append(results, entry) 606 } 607 } 608 609 if identifierType&(IdentifierUnknown|IdentifierBootscript) > 0 { 610 bootscripts, err := c.LookUpBootscripts(needle, false) 611 if err != nil { 612 return ScalewayResolverResults{}, err 613 } 614 for _, result := range bootscripts { 615 entry, err := NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, IdentifierBootscript) 616 if err != nil { 617 return ScalewayResolverResults{}, err 618 } 619 entry.ComputeRankMatch(needle) 620 results = append(results, entry) 621 } 622 } 623 return results, nil 624 } 625 626 // InsertServer registers a server in the cache 627 func (c *ScalewayCache) InsertServer(identifier, region, arch, owner, name string) { 628 c.Lock.Lock() 629 defer c.Lock.Unlock() 630 631 fields, exists := c.Servers[identifier] 632 if !exists || fields[CacheTitle] != name { 633 c.Servers[identifier] = [CacheMaxfield]string{region, arch, owner, name} 634 c.Modified = true 635 } 636 } 637 638 // RemoveServer removes a server from the cache 639 func (c *ScalewayCache) RemoveServer(identifier string) { 640 c.Lock.Lock() 641 defer c.Lock.Unlock() 642 643 delete(c.Servers, identifier) 644 c.Modified = true 645 } 646 647 // ClearServers removes all servers from the cache 648 func (c *ScalewayCache) ClearServers() { 649 c.Lock.Lock() 650 defer c.Lock.Unlock() 651 652 c.Servers = make(map[string][CacheMaxfield]string) 653 c.Modified = true 654 } 655 656 // InsertImage registers an image in the cache 657 func (c *ScalewayCache) InsertImage(identifier, region, arch, owner, name, marketPlaceUUID string) { 658 c.Lock.Lock() 659 defer c.Lock.Unlock() 660 661 fields, exists := c.Images[identifier] 662 if !exists || fields[CacheTitle] != name { 663 c.Images[identifier] = [CacheMaxfield]string{region, arch, owner, name, marketPlaceUUID} 664 c.Modified = true 665 } 666 } 667 668 // RemoveImage removes a server from the cache 669 func (c *ScalewayCache) RemoveImage(identifier string) { 670 c.Lock.Lock() 671 defer c.Lock.Unlock() 672 673 delete(c.Images, identifier) 674 c.Modified = true 675 } 676 677 // ClearImages removes all images from the cache 678 func (c *ScalewayCache) ClearImages() { 679 c.Lock.Lock() 680 defer c.Lock.Unlock() 681 682 c.Images = make(map[string][CacheMaxfield]string) 683 c.Modified = true 684 } 685 686 // InsertSnapshot registers an snapshot in the cache 687 func (c *ScalewayCache) InsertSnapshot(identifier, region, arch, owner, name string) { 688 c.Lock.Lock() 689 defer c.Lock.Unlock() 690 691 fields, exists := c.Snapshots[identifier] 692 if !exists || fields[CacheTitle] != name { 693 c.Snapshots[identifier] = [CacheMaxfield]string{region, arch, owner, name} 694 c.Modified = true 695 } 696 } 697 698 // RemoveSnapshot removes a server from the cache 699 func (c *ScalewayCache) RemoveSnapshot(identifier string) { 700 c.Lock.Lock() 701 defer c.Lock.Unlock() 702 703 delete(c.Snapshots, identifier) 704 c.Modified = true 705 } 706 707 // ClearSnapshots removes all snapshots from the cache 708 func (c *ScalewayCache) ClearSnapshots() { 709 c.Lock.Lock() 710 defer c.Lock.Unlock() 711 712 c.Snapshots = make(map[string][CacheMaxfield]string) 713 c.Modified = true 714 } 715 716 // InsertVolume registers an volume in the cache 717 func (c *ScalewayCache) InsertVolume(identifier, region, arch, owner, name string) { 718 c.Lock.Lock() 719 defer c.Lock.Unlock() 720 721 fields, exists := c.Volumes[identifier] 722 if !exists || fields[CacheTitle] != name { 723 c.Volumes[identifier] = [CacheMaxfield]string{region, arch, owner, name} 724 c.Modified = true 725 } 726 } 727 728 // RemoveVolume removes a server from the cache 729 func (c *ScalewayCache) RemoveVolume(identifier string) { 730 c.Lock.Lock() 731 defer c.Lock.Unlock() 732 733 delete(c.Volumes, identifier) 734 c.Modified = true 735 } 736 737 // ClearVolumes removes all volumes from the cache 738 func (c *ScalewayCache) ClearVolumes() { 739 c.Lock.Lock() 740 defer c.Lock.Unlock() 741 742 c.Volumes = make(map[string][CacheMaxfield]string) 743 c.Modified = true 744 } 745 746 // InsertBootscript registers an bootscript in the cache 747 func (c *ScalewayCache) InsertBootscript(identifier, region, arch, owner, name string) { 748 c.Lock.Lock() 749 defer c.Lock.Unlock() 750 751 fields, exists := c.Bootscripts[identifier] 752 if !exists || fields[CacheTitle] != name { 753 c.Bootscripts[identifier] = [CacheMaxfield]string{region, arch, owner, name} 754 c.Modified = true 755 } 756 } 757 758 // RemoveBootscript removes a bootscript from the cache 759 func (c *ScalewayCache) RemoveBootscript(identifier string) { 760 c.Lock.Lock() 761 defer c.Lock.Unlock() 762 763 delete(c.Bootscripts, identifier) 764 c.Modified = true 765 } 766 767 // ClearBootscripts removes all bootscripts from the cache 768 func (c *ScalewayCache) ClearBootscripts() { 769 c.Lock.Lock() 770 defer c.Lock.Unlock() 771 772 c.Bootscripts = make(map[string][CacheMaxfield]string) 773 c.Modified = true 774 } 775 776 // GetNbServers returns the number of servers in the cache 777 func (c *ScalewayCache) GetNbServers() int { 778 c.Lock.Lock() 779 defer c.Lock.Unlock() 780 781 return len(c.Servers) 782 } 783 784 // GetNbImages returns the number of images in the cache 785 func (c *ScalewayCache) GetNbImages() int { 786 c.Lock.Lock() 787 defer c.Lock.Unlock() 788 789 return len(c.Images) 790 } 791 792 // GetNbSnapshots returns the number of snapshots in the cache 793 func (c *ScalewayCache) GetNbSnapshots() int { 794 c.Lock.Lock() 795 defer c.Lock.Unlock() 796 797 return len(c.Snapshots) 798 } 799 800 // GetNbVolumes returns the number of volumes in the cache 801 func (c *ScalewayCache) GetNbVolumes() int { 802 c.Lock.Lock() 803 defer c.Lock.Unlock() 804 805 return len(c.Volumes) 806 } 807 808 // GetNbBootscripts returns the number of bootscripts in the cache 809 func (c *ScalewayCache) GetNbBootscripts() int { 810 c.Lock.Lock() 811 defer c.Lock.Unlock() 812 813 return len(c.Bootscripts) 814 }