sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/virtualmachineimages/cache.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package virtualmachineimages 18 19 import ( 20 "context" 21 "sync" 22 "time" 23 24 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 25 "github.com/pkg/errors" 26 "sigs.k8s.io/cluster-api-provider-azure/azure" 27 "sigs.k8s.io/cluster-api-provider-azure/util/cache/ttllru" 28 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 29 ) 30 31 // Key contains the fields necessary to locate a VM image list resource. 32 type Key struct { 33 location string 34 publisher string 35 offer string 36 sku string 37 } 38 39 // Cache stores VM image list resources. 40 type Cache struct { 41 client Client 42 data map[Key]armcompute.VirtualMachineImagesClientListResponse 43 } 44 45 // Cacher allows getting items from and adding them to a cache. 46 type Cacher interface { 47 Get(key interface{}) (value interface{}, ok bool) 48 Add(key interface{}, value interface{}) bool 49 } 50 51 var ( 52 _ Client = &AzureClient{} 53 doOnce sync.Once 54 clientCache Cacher 55 ) 56 57 // newCache instantiates a cache. 58 func newCache(auth azure.Authorizer) (*Cache, error) { 59 client, err := NewClient(auth) 60 if err != nil { 61 return nil, err 62 } 63 return &Cache{ 64 client: client, 65 }, nil 66 } 67 68 // GetCache either creates a new VM images cache or returns the existing one. 69 func GetCache(auth azure.Authorizer) (*Cache, error) { 70 var err error 71 doOnce.Do(func() { 72 clientCache, err = ttllru.New(128, 1*time.Hour) 73 }) 74 if err != nil { 75 return nil, errors.Wrap(err, "failed creating LRU cache for VM images") 76 } 77 78 key := auth.HashKey() 79 c, ok := clientCache.Get(key) 80 if ok { 81 return c.(*Cache), nil 82 } 83 84 c, err = newCache(auth) 85 if err != nil { 86 return nil, err 87 } 88 _ = clientCache.Add(key, c) 89 return c.(*Cache), nil 90 } 91 92 // refresh fetches a VM image list resource from Azure and stores it in the cache. 93 func (c *Cache) refresh(ctx context.Context, key Key) error { 94 ctx, _, done := tele.StartSpanWithLogger(ctx, "virtualmachineimages.Cache.refresh") 95 defer done() 96 97 data, err := c.client.List(ctx, key.location, key.publisher, key.offer, key.sku) 98 if err != nil { 99 return errors.Wrap(err, "failed to refresh VM images cache") 100 } 101 102 c.data[key] = data 103 104 return nil 105 } 106 107 // Get returns a VM image list resource in a location given a publisher, offer, and sku. 108 func (c *Cache) Get(ctx context.Context, location, publisher, offer, sku string) (armcompute.VirtualMachineImagesClientListResponse, error) { 109 ctx, log, done := tele.StartSpanWithLogger(ctx, "virtualmachineimages.Cache.Get") 110 defer done() 111 112 if c.data == nil { 113 c.data = make(map[Key]armcompute.VirtualMachineImagesClientListResponse) 114 } 115 116 key := Key{ 117 location: location, 118 publisher: publisher, 119 offer: offer, 120 sku: sku, 121 } 122 123 if _, ok := c.data[key]; !ok { 124 log.V(4).Info("VM images cache miss", "location", key.location, "publisher", key.publisher, "offer", key.offer, "sku", key.sku) 125 if err := c.refresh(ctx, key); err != nil { 126 return armcompute.VirtualMachineImagesClientListResponse{}, err 127 } 128 } else { 129 log.V(4).Info("VM images cache hit", "location", key.location, "publisher", key.publisher, "offer", key.offer, "sku", key.sku) 130 } 131 132 return c.data[key], nil 133 }