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  }