// Copyright 2018 Envoyproxy Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cache import ( "errors" "fmt" ) // Resources is a versioned group of resources. type Resources struct { // Version information. Version string // Items in the group. Items map[string]Resource } // IndexResourcesByName creates a map from the resource name to the resource. func IndexResourcesByName(items []Resource) map[string]Resource { indexed := make(map[string]Resource, len(items)) for _, item := range items { indexed[GetResourceName(item)] = item } return indexed } // NewResources creates a new resource group. func NewResources(version string, items []Resource) Resources { return Resources{ Version: version, Items: IndexResourcesByName(items), } } // Snapshot is an internally consistent snapshot of xDS resources. // Consistentcy is important for the convergence as different resource types // from the snapshot may be delivered to the proxy in arbitrary order. type Snapshot struct { // Endpoints are items in the EDS response payload. Endpoints Resources // Clusters are items in the CDS response payload. Clusters Resources // Routes are items in the RDS response payload. Routes Resources // Listeners are items in the LDS response payload. Listeners Resources } // NewSnapshot creates a snapshot from response types and a version. func NewSnapshot(version string, endpoints []Resource, clusters []Resource, routes []Resource, listeners []Resource) Snapshot { return Snapshot{ Endpoints: NewResources(version, endpoints), Clusters: NewResources(version, clusters), Routes: NewResources(version, routes), Listeners: NewResources(version, listeners), } } // Consistent check verifies that the dependent resources are exactly listed in the // snapshot: // - all EDS resources are listed by name in CDS resources // - all RDS resources are listed by name in LDS resources // // Note that clusters and listeners are requested without name references, so // Envoy will accept the snapshot list of clusters as-is even if it does not match // all references found in xDS. func (s *Snapshot) Consistent() error { if s == nil { return errors.New("nil snapshot") } endpoints := GetResourceReferences(s.Clusters.Items) if len(endpoints) != len(s.Endpoints.Items) { return fmt.Errorf("mismatched endpoint reference and resource lengths: %v != %d", endpoints, len(s.Endpoints.Items)) } if err := superset(endpoints, s.Endpoints.Items); err != nil { return err } routes := GetResourceReferences(s.Listeners.Items) if len(routes) != len(s.Routes.Items) { return fmt.Errorf("mismatched route reference and resource lengths: %v != %d", routes, len(s.Routes.Items)) } return superset(routes, s.Routes.Items) } // GetResources selects snapshot resources by type. func (s *Snapshot) GetResources(typ string) map[string]Resource { if s == nil { return nil } switch typ { case EndpointType: return s.Endpoints.Items case ClusterType: return s.Clusters.Items case RouteType: return s.Routes.Items case ListenerType: return s.Listeners.Items } return nil } // GetVersion returns the version for a resource type. func (s *Snapshot) GetVersion(typ string) string { if s == nil { return "" } switch typ { case EndpointType: return s.Endpoints.Version case ClusterType: return s.Clusters.Version case RouteType: return s.Routes.Version case ListenerType: return s.Listeners.Version } return "" }