istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/aggregate/config.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package aggregate implements a read-only aggregator for config stores.
    16  package aggregate
    17  
    18  import (
    19  	"errors"
    20  
    21  	"k8s.io/apimachinery/pkg/types"
    22  
    23  	"istio.io/istio/pilot/pkg/model"
    24  	"istio.io/istio/pkg/config"
    25  	"istio.io/istio/pkg/config/schema/collection"
    26  	"istio.io/istio/pkg/slices"
    27  	"istio.io/istio/pkg/util/sets"
    28  )
    29  
    30  var errorUnsupported = errors.New("unsupported operation: the config aggregator is read-only")
    31  
    32  // makeStore creates an aggregate config store from several config stores and
    33  // unifies their descriptors
    34  func makeStore(stores []model.ConfigStore, writer model.ConfigStore) (model.ConfigStore, error) {
    35  	union := collection.NewSchemasBuilder()
    36  	storeTypes := make(map[config.GroupVersionKind][]model.ConfigStore)
    37  	for _, store := range stores {
    38  		for _, s := range store.Schemas().All() {
    39  			if len(storeTypes[s.GroupVersionKind()]) == 0 {
    40  				if err := union.Add(s); err != nil {
    41  					return nil, err
    42  				}
    43  			}
    44  			storeTypes[s.GroupVersionKind()] = append(storeTypes[s.GroupVersionKind()], store)
    45  		}
    46  	}
    47  
    48  	schemas := union.Build()
    49  	if err := schemas.Validate(); err != nil {
    50  		return nil, err
    51  	}
    52  	result := &store{
    53  		schemas: schemas,
    54  		stores:  storeTypes,
    55  		writer:  writer,
    56  	}
    57  
    58  	return result, nil
    59  }
    60  
    61  // MakeWriteableCache creates an aggregate config store cache from several config store caches. An additional
    62  // `writer` config store is passed, which may or may not be part of `caches`.
    63  func MakeWriteableCache(caches []model.ConfigStoreController, writer model.ConfigStore) (model.ConfigStoreController, error) {
    64  	stores := make([]model.ConfigStore, 0, len(caches))
    65  	for _, cache := range caches {
    66  		stores = append(stores, cache)
    67  	}
    68  	store, err := makeStore(stores, writer)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	return &storeCache{
    73  		ConfigStore: store,
    74  		caches:      caches,
    75  	}, nil
    76  }
    77  
    78  // MakeCache creates an aggregate config store cache from several config store
    79  // caches.
    80  func MakeCache(caches []model.ConfigStoreController) (model.ConfigStoreController, error) {
    81  	return MakeWriteableCache(caches, nil)
    82  }
    83  
    84  type store struct {
    85  	// schemas is the unified
    86  	schemas collection.Schemas
    87  
    88  	// stores is a mapping from config type to a store
    89  	stores map[config.GroupVersionKind][]model.ConfigStore
    90  
    91  	writer model.ConfigStore
    92  }
    93  
    94  func (cr *store) Schemas() collection.Schemas {
    95  	return cr.schemas
    96  }
    97  
    98  // Get the first config found in the stores.
    99  func (cr *store) Get(typ config.GroupVersionKind, name, namespace string) *config.Config {
   100  	for _, store := range cr.stores[typ] {
   101  		config := store.Get(typ, name, namespace)
   102  		if config != nil {
   103  			return config
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  // List all configs in the stores.
   110  func (cr *store) List(typ config.GroupVersionKind, namespace string) []config.Config {
   111  	stores := cr.stores[typ]
   112  	if len(stores) == 0 {
   113  		return nil
   114  	}
   115  
   116  	var (
   117  		configs      []config.Config
   118  		storeConfigs = make([][]config.Config, 0, len(stores))
   119  		configCnt    int
   120  	)
   121  
   122  	for _, store := range stores {
   123  		curConfigs := store.List(typ, namespace)
   124  		storeConfigs = append(storeConfigs, curConfigs)
   125  		configCnt += len(curConfigs)
   126  	}
   127  
   128  	configs = make([]config.Config, 0, configCnt)
   129  	// Used to remove duplicated config
   130  	configMap := sets.NewWithLength[types.NamespacedName](configCnt)
   131  	for _, curConfigs := range storeConfigs {
   132  		configs = append(configs, curConfigs...)
   133  	}
   134  	configs = slices.FilterInPlace[config.Config](configs, func(cfg config.Config) bool {
   135  		return !configMap.InsertContains(cfg.NamespacedName())
   136  	})
   137  
   138  	return configs
   139  }
   140  
   141  func (cr *store) Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error {
   142  	if cr.writer == nil {
   143  		return errorUnsupported
   144  	}
   145  	return cr.writer.Delete(typ, name, namespace, resourceVersion)
   146  }
   147  
   148  func (cr *store) Create(c config.Config) (string, error) {
   149  	if cr.writer == nil {
   150  		return "", errorUnsupported
   151  	}
   152  	return cr.writer.Create(c)
   153  }
   154  
   155  func (cr *store) Update(c config.Config) (string, error) {
   156  	if cr.writer == nil {
   157  		return "", errorUnsupported
   158  	}
   159  	return cr.writer.Update(c)
   160  }
   161  
   162  func (cr *store) UpdateStatus(c config.Config) (string, error) {
   163  	if cr.writer == nil {
   164  		return "", errorUnsupported
   165  	}
   166  	return cr.writer.UpdateStatus(c)
   167  }
   168  
   169  func (cr *store) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) {
   170  	if cr.writer == nil {
   171  		return "", errorUnsupported
   172  	}
   173  	return cr.writer.Patch(orig, patchFn)
   174  }
   175  
   176  type storeCache struct {
   177  	model.ConfigStore
   178  	caches []model.ConfigStoreController
   179  }
   180  
   181  func (cr *storeCache) HasSynced() bool {
   182  	for _, cache := range cr.caches {
   183  		if !cache.HasSynced() {
   184  			return false
   185  		}
   186  	}
   187  	return true
   188  }
   189  
   190  func (cr *storeCache) RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler) {
   191  	for _, cache := range cr.caches {
   192  		if _, exists := cache.Schemas().FindByGroupVersionKind(kind); exists {
   193  			cache.RegisterEventHandler(kind, handler)
   194  		}
   195  	}
   196  }
   197  
   198  func (cr *storeCache) Run(stop <-chan struct{}) {
   199  	for _, cache := range cr.caches {
   200  		go cache.Run(stop)
   201  	}
   202  	<-stop
   203  }