diff --git a/go.sum b/go.sum index f4d16e829b..bf03e1a6ca 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158 h1:CevA8fI91PAnP8vpnXuB8ZYAZ5wqY86nAbxfgK8tWO4= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= diff --git a/internal/example/resource.go b/internal/example/resource.go index 05b2e93238..4b0ba4ceff 100644 --- a/internal/example/resource.go +++ b/internal/example/resource.go @@ -165,14 +165,12 @@ func makeConfigSource() *core.ConfigSource { } func GenerateSnapshot() cache.Snapshot { - return cache.NewSnapshot( - "1", - []types.Resource{}, // endpoints - []types.Resource{makeCluster(ClusterName)}, - []types.Resource{makeRoute(RouteName, ClusterName)}, - []types.Resource{makeHTTPListener(ListenerName, RouteName)}, - []types.Resource{}, // runtimes - []types.Resource{}, // secrets - []types.Resource{}, // extension configs + snap, _ := cache.NewSnapshot("1", + map[resource.Type][]types.Resource{ + resource.ClusterType: {makeCluster(ClusterName)}, + resource.RouteType: {makeRoute(RouteName, ClusterName)}, + resource.ListenerType: {makeHTTPListener(ListenerName, RouteName)}, + }, ) + return snap } diff --git a/pkg/cache/v3/delta_test.go b/pkg/cache/v3/delta_test.go index 254aa13b53..67cc2e807a 100644 --- a/pkg/cache/v3/delta_test.go +++ b/pkg/cache/v3/delta_test.go @@ -182,7 +182,10 @@ func TestConcurrentSetDeltaWatch(t *testing.T) { id := fmt.Sprintf("%d", i%2) responses := make(chan cache.DeltaResponse, 1) if i < 25 { - snap := cache.Snapshot{} + snap, err := cache.NewSnapshot("", map[rsrc.Type][]types.Resource{}) + if err != nil { + t.Fatal(err) + } snap.Resources[types.Endpoint] = cache.NewResources(version, []types.Resource{resource.MakeEndpoint(clusterName, uint32(i))}) if err := c.SetSnapshot(context.Background(), key, snap); err != nil { t.Fatalf("snapshot failed: %s", err) diff --git a/pkg/cache/v3/resource.go b/pkg/cache/v3/resource.go index 503f1a3bfd..df3ff22b93 100644 --- a/pkg/cache/v3/resource.go +++ b/pkg/cache/v3/resource.go @@ -35,7 +35,7 @@ import ( ) // GetResponseType returns the enumeration for a valid xDS type URL -func GetResponseType(typeURL string) types.ResponseType { +func GetResponseType(typeURL resource.Type) types.ResponseType { switch typeURL { case resource.EndpointType: return types.Endpoint diff --git a/pkg/cache/v3/resources.go b/pkg/cache/v3/resources.go new file mode 100644 index 0000000000..92df743f66 --- /dev/null +++ b/pkg/cache/v3/resources.go @@ -0,0 +1,47 @@ +package cache + +import "github.com/envoyproxy/go-control-plane/pkg/cache/types" + +// Resources is a versioned group of resources. +type Resources struct { + // Version information. + Version string + + // Items in the group indexed by name. + Items map[string]types.ResourceWithTTL +} + +// IndexResourcesByName creates a map from the resource name to the resource. +func IndexResourcesByName(items []types.ResourceWithTTL) map[string]types.ResourceWithTTL { + indexed := make(map[string]types.ResourceWithTTL) + for _, item := range items { + indexed[GetResourceName(item.Resource)] = item + } + return indexed +} + +// IndexRawResourcesByName creates a map from the resource name to the resource. +func IndexRawResourcesByName(items []types.Resource) map[string]types.Resource { + indexed := make(map[string]types.Resource) + for _, item := range items { + indexed[GetResourceName(item)] = item + } + return indexed +} + +// NewResources creates a new resource group. +func NewResources(version string, items []types.Resource) Resources { + itemsWithTTL := []types.ResourceWithTTL{} + for _, item := range items { + itemsWithTTL = append(itemsWithTTL, types.ResourceWithTTL{Resource: item}) + } + return NewResourcesWithTTL(version, itemsWithTTL) +} + +// NewResourcesWithTTL creates a new resource group. +func NewResourcesWithTTL(version string, items []types.ResourceWithTTL) Resources { + return Resources{ + Version: version, + Items: IndexResourcesByName(items), + } +} diff --git a/pkg/cache/v3/resources_test.go b/pkg/cache/v3/resources_test.go new file mode 100644 index 0000000000..5751f3a518 --- /dev/null +++ b/pkg/cache/v3/resources_test.go @@ -0,0 +1,84 @@ +package cache_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/envoyproxy/go-control-plane/pkg/cache/types" + "github.com/envoyproxy/go-control-plane/pkg/cache/v3" +) + +func TestIndexResourcesByName(t *testing.T) { + tests := []struct { + name string + resources []types.ResourceWithTTL + want map[string]types.ResourceWithTTL + }{ + { + name: "empty", + resources: nil, + want: map[string]types.ResourceWithTTL{}, + }, + { + name: "more than one", + resources: []types.ResourceWithTTL{ + {Resource: testEndpoint, TTL: &ttl}, + {Resource: testRoute, TTL: &ttl}, + }, + want: map[string]types.ResourceWithTTL{ + "cluster0": {Resource: testEndpoint, TTL: &ttl}, + "route0": {Resource: testRoute, TTL: &ttl}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := cache.IndexResourcesByName(tt.resources) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestIndexRawResourceByName(t *testing.T) { + tests := []struct { + name string + resources []types.Resource + want map[string]types.Resource + }{ + { + name: "empty", + resources: nil, + want: map[string]types.Resource{}, + }, + { + name: "more than one", + resources: []types.Resource{ + testEndpoint, + testRoute, + }, + want: map[string]types.Resource{ + "cluster0": testEndpoint, + "route0": testRoute, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := cache.IndexRawResourcesByName(tt.resources) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestNewResources(t *testing.T) { + resources := cache.NewResources("x", []types.Resource{ + testEndpoint, + testRoute, + }) + + assert.NotNil(t, resources.Items) + assert.Equal(t, "x", resources.Version) +} diff --git a/pkg/cache/v3/simple.go b/pkg/cache/v3/simple.go index 7e2284c762..a8cb690b09 100644 --- a/pkg/cache/v3/simple.go +++ b/pkg/cache/v3/simple.go @@ -157,7 +157,7 @@ func (cache *snapshotCache) sendHeartbeats(ctx context.Context, node string) { for id, watch := range info.watches { // Respond with the current version regardless of whether the version has changed. version := snapshot.GetVersion(watch.Request.TypeUrl) - resources := snapshot.GetResourcesAndTtl(watch.Request.TypeUrl) + resources := snapshot.GetResourcesAndTTL(watch.Request.TypeUrl) // TODO(snowp): Construct this once per type instead of once per watch. resourcesWithTTL := map[string]types.ResourceWithTTL{} @@ -201,7 +201,7 @@ func (cache *snapshotCache) SetSnapshot(ctx context.Context, node string, snapsh if cache.log != nil { cache.log.Debugf("respond open watch %d%v with new version %q", id, watch.Request.ResourceNames, version) } - resources := snapshot.GetResourcesAndTtl(watch.Request.TypeUrl) + resources := snapshot.GetResourcesAndTTL(watch.Request.TypeUrl) err := cache.respond(ctx, watch.Request, watch.Response, resources, version, false) if err != nil { return err @@ -321,7 +321,7 @@ func (cache *snapshotCache) CreateWatch(request *Request, value chan Response) f } // otherwise, the watch may be responded immediately - resources := snapshot.GetResourcesAndTtl(request.TypeUrl) + resources := snapshot.GetResourcesAndTTL(request.TypeUrl) _ = cache.respond(context.Background(), request, value, resources, version, false) return nil @@ -492,7 +492,7 @@ func (cache *snapshotCache) Fetch(ctx context.Context, request *Request) (Respon return nil, &types.SkipFetchError{} } - resources := snapshot.GetResourcesAndTtl(request.TypeUrl) + resources := snapshot.GetResourcesAndTTL(request.TypeUrl) out := createResponse(ctx, request, resources, version, false) return out, nil } diff --git a/pkg/cache/v3/simple_test.go b/pkg/cache/v3/simple_test.go index ed9bda7a8d..487ec17241 100644 --- a/pkg/cache/v3/simple_test.go +++ b/pkg/cache/v3/simple_test.go @@ -49,24 +49,26 @@ var ( version = "x" version2 = "y" - snapshot = cache.NewSnapshot(version, - []types.Resource{testEndpoint}, - []types.Resource{testCluster}, - []types.Resource{testRoute}, - []types.Resource{testListener}, - []types.Resource{testRuntime}, - []types.Resource{testSecret[0]}, - []types.Resource{testExtensionConfig}) - - ttl = 2 * time.Second - - snapshotWithTTL = cache.NewSnapshotWithTtls(version, - []types.ResourceWithTTL{{Resource: testEndpoint, TTL: &ttl}}, - []types.ResourceWithTTL{{Resource: testCluster}}, - []types.ResourceWithTTL{{Resource: testRoute}}, - []types.ResourceWithTTL{{Resource: testListener}}, - []types.ResourceWithTTL{{Resource: testRuntime}}, - []types.ResourceWithTTL{{Resource: testSecret[0]}}) + snapshot, _ = cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + rsrc.EndpointType: {testEndpoint}, + rsrc.ClusterType: {testCluster}, + rsrc.RouteType: {testRoute}, + rsrc.ListenerType: {testListener}, + rsrc.RuntimeType: {testRuntime}, + rsrc.SecretType: {testSecret[0]}, + rsrc.ExtensionConfigType: {testExtensionConfig}, + }) + + ttl = 2 * time.Second + snapshotWithTTL, _ = cache.NewSnapshotWithTTLs(version, map[rsrc.Type][]types.ResourceWithTTL{ + rsrc.EndpointType: {{Resource: testEndpoint, TTL: &ttl}}, + rsrc.ClusterType: {{Resource: testCluster}}, + rsrc.RouteType: {{Resource: testRoute}}, + rsrc.ListenerType: {{Resource: testListener}}, + rsrc.RuntimeType: {{Resource: testRuntime}}, + rsrc.SecretType: {{Resource: testSecret[0]}}, + rsrc.ExtensionConfigType: {{Resource: testExtensionConfig}}, + }) names = map[string][]string{ rsrc.EndpointType: {clusterName}, @@ -94,7 +96,7 @@ func (log logger) Infof(format string, args ...interface{}) { log.t.Logf(format func (log logger) Warnf(format string, args ...interface{}) { log.t.Logf(format, args...) } func (log logger) Errorf(format string, args ...interface{}) { log.t.Logf(format, args...) } -func TestSnapshotCacheWithTtl(t *testing.T) { +func TestSnapshotCacheWithTTL(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() c := cache.NewSnapshotCacheWithHeartbeating(ctx, true, group{}, logger{t: t}, time.Second) @@ -128,8 +130,8 @@ func TestSnapshotCacheWithTtl(t *testing.T) { if gotVersion, _ := out.GetVersion(); gotVersion != version { t.Errorf("got version %q, want %q", gotVersion, version) } - if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTtl(typ)) { - t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshotWithTTL.GetResourcesAndTtl(typ)) + if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTTL(typ)) { + t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshotWithTTL.GetResourcesAndTTL(typ)) } case <-time.After(2 * time.Second): t.Errorf("failed to receive snapshot response") @@ -156,11 +158,11 @@ func TestSnapshotCacheWithTtl(t *testing.T) { if gotVersion, _ := out.GetVersion(); gotVersion != version { t.Errorf("got version %q, want %q", gotVersion, version) } - if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTtl(typ)) { + if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTTL(typ)) { t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshotWithTTL.GetResources(typ)) } - if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTtl(typ)) { + if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshotWithTTL.GetResourcesAndTTL(typ)) { t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshotWithTTL.GetResources(typ)) } @@ -222,8 +224,8 @@ func TestSnapshotCache(t *testing.T) { if gotVersion, _ := out.GetVersion(); gotVersion != version { t.Errorf("got version %q, want %q", gotVersion, version) } - if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshot.GetResourcesAndTtl(typ)) { - t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot.GetResourcesAndTtl(typ)) + if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshot.GetResourcesAndTTL(typ)) { + t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot.GetResourcesAndTTL(typ)) } case <-time.After(time.Second): t.Fatal("failed to receive snapshot response") @@ -280,8 +282,8 @@ func TestSnapshotCacheWatch(t *testing.T) { if gotVersion, _ := out.GetVersion(); gotVersion != version { t.Errorf("got version %q, want %q", gotVersion, version) } - if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshot.GetResourcesAndTtl(typ)) { - t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot.GetResourcesAndTtl(typ)) + if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshot.GetResourcesAndTTL(typ)) { + t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot.GetResourcesAndTTL(typ)) } case <-time.After(time.Second): t.Fatal("failed to receive snapshot response") @@ -315,7 +317,7 @@ func TestSnapshotCacheWatch(t *testing.T) { t.Errorf("got version %q, want %q", gotVersion, version2) } if !reflect.DeepEqual(cache.IndexResourcesByName(out.(*cache.RawResponse).Resources), snapshot2.Resources[types.Endpoint].Items) { - t.Errorf("get resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot2.Resources[types.Endpoint].Items) + t.Errorf("got resources %v, want %v", out.(*cache.RawResponse).Resources, snapshot2.Resources[types.Endpoint].Items) } case <-time.After(time.Second): t.Fatal("failed to receive snapshot response") diff --git a/pkg/cache/v3/snapshot.go b/pkg/cache/v3/snapshot.go index 8e608037d4..abe573cccb 100644 --- a/pkg/cache/v3/snapshot.go +++ b/pkg/cache/v3/snapshot.go @@ -19,67 +19,9 @@ import ( "fmt" "github.com/envoyproxy/go-control-plane/pkg/cache/types" + "github.com/envoyproxy/go-control-plane/pkg/resource/v3" ) -// Resources is a versioned group of resources. -type Resources struct { - // Version information. - Version string - - // Items in the group indexed by name. - Items map[string]types.ResourceWithTTL -} - -// DeltaResources is a versioned group of resources which also contains individual resource versions per the incremental xDS protocol -type DeltaResources struct { - // Version information - SystemVersion string - - // Items in the group indexed by name - Items resourceItems -} - -// resourceItems contain the lower level versioned resource map -type resourceItems struct { - Version string - Items map[string]types.ResourceWithTTL -} - -// IndexResourcesByName creates a map from the resource name to the resource. -func IndexResourcesByName(items []types.ResourceWithTTL) map[string]types.ResourceWithTTL { - indexed := make(map[string]types.ResourceWithTTL) - for _, item := range items { - indexed[GetResourceName(item.Resource)] = item - } - return indexed -} - -// IndexRawResourcesByName creates a map from the resource name to the resource. -func IndexRawResourcesByName(items []types.Resource) map[string]types.Resource { - indexed := make(map[string]types.Resource) - for _, item := range items { - indexed[GetResourceName(item)] = item - } - return indexed -} - -// NewResources creates a new resource group. -func NewResources(version string, items []types.Resource) Resources { - itemsWithTTL := []types.ResourceWithTTL{} - for _, item := range items { - itemsWithTTL = append(itemsWithTTL, types.ResourceWithTTL{Resource: item}) - } - return NewResourcesWithTtl(version, itemsWithTTL) -} - -// NewResources creates a new resource group. -func NewResourcesWithTtl(version string, items []types.ResourceWithTTL) Resources { // nolint:golint,revive - return Resources{ - Version: version, - Items: IndexResourcesByName(items), - } -} - // Snapshot is an internally consistent snapshot of xDS resources. // Consistency is important for the convergence as different resource types // from the snapshot may be delivered to the proxy in arbitrary order. @@ -94,65 +36,37 @@ type Snapshot struct { } // NewSnapshot creates a snapshot from response types and a version. -func NewSnapshot(version string, - endpoints []types.Resource, - clusters []types.Resource, - routes []types.Resource, - listeners []types.Resource, - runtimes []types.Resource, - secrets []types.Resource, - extensionConfigs []types.Resource) Snapshot { - return NewSnapshotWithResources(version, SnapshotResources{ - Endpoints: endpoints, - Clusters: clusters, - Routes: routes, - Listeners: listeners, - Runtimes: runtimes, - Secrets: secrets, - ExtensionConfigs: extensionConfigs, - }) -} +// The resources map is keyed off the type URL of a resource, followed by the slice of resource objects. +func NewSnapshot(version string, resources map[resource.Type][]types.Resource) (Snapshot, error) { + out := Snapshot{} -// SnapshotResources contains the resources to construct a snapshot from. -type SnapshotResources struct { - Endpoints []types.Resource - Clusters []types.Resource - Routes []types.Resource - Listeners []types.Resource - Runtimes []types.Resource - Secrets []types.Resource - ExtensionConfigs []types.Resource -} + for typ, resource := range resources { + index := GetResponseType(typ) + if index == types.UnknownType { + return out, errors.New("unknown resource type: " + typ) + } -// NewSnapshotWithResources creates a snapshot from response types and a version. -func NewSnapshotWithResources(version string, resources SnapshotResources) Snapshot { - out := Snapshot{} - out.Resources[types.Endpoint] = NewResources(version, resources.Endpoints) - out.Resources[types.Cluster] = NewResources(version, resources.Clusters) - out.Resources[types.Route] = NewResources(version, resources.Routes) - out.Resources[types.Listener] = NewResources(version, resources.Listeners) - out.Resources[types.Runtime] = NewResources(version, resources.Runtimes) - out.Resources[types.Secret] = NewResources(version, resources.Secrets) - out.Resources[types.ExtensionConfig] = NewResources(version, resources.ExtensionConfigs) + out.Resources[index] = NewResources(version, resource) + } - return out + return out, nil } -func NewSnapshotWithTtls(version string, - endpoints []types.ResourceWithTTL, - clusters []types.ResourceWithTTL, - routes []types.ResourceWithTTL, - listeners []types.ResourceWithTTL, - runtimes []types.ResourceWithTTL, - secrets []types.ResourceWithTTL) Snapshot { +// NewSnapshotWithTTLs creates a snapshot of ResourceWithTTLs. +// The resources map is keyed off the type URL of a resource, followed by the slice of resource objects. +func NewSnapshotWithTTLs(version string, resources map[resource.Type][]types.ResourceWithTTL) (Snapshot, error) { out := Snapshot{} - out.Resources[types.Endpoint] = NewResourcesWithTtl(version, endpoints) - out.Resources[types.Cluster] = NewResourcesWithTtl(version, clusters) - out.Resources[types.Route] = NewResourcesWithTtl(version, routes) - out.Resources[types.Listener] = NewResourcesWithTtl(version, listeners) - out.Resources[types.Runtime] = NewResourcesWithTtl(version, runtimes) - out.Resources[types.Secret] = NewResourcesWithTtl(version, secrets) - return out + + for typ, resource := range resources { + index := GetResponseType(typ) + if index == types.UnknownType { + return out, errors.New("unknown resource type: " + typ) + } + + out.Resources[index] = NewResourcesWithTTL(version, resource) + } + + return out, nil } // Consistent check verifies that the dependent resources are exactly listed in the @@ -183,8 +97,8 @@ func (s *Snapshot) Consistent() error { } // GetResources selects snapshot resources by type, returning the map of resources. -func (s *Snapshot) GetResources(typeURL string) map[string]types.Resource { - resources := s.GetResourcesAndTtl(typeURL) +func (s *Snapshot) GetResources(typeURL resource.Type) map[string]types.Resource { + resources := s.GetResourcesAndTTL(typeURL) if resources == nil { return nil } @@ -198,8 +112,8 @@ func (s *Snapshot) GetResources(typeURL string) map[string]types.Resource { return withoutTTL } -// GetResourcesAndTtl selects snapshot resources by type, returning the map of resources and the associated TTL. -func (s *Snapshot) GetResourcesAndTtl(typeURL string) map[string]types.ResourceWithTTL { // nolint:golint,revive +// GetResourcesAndTTL selects snapshot resources by type, returning the map of resources and the associated TTL. +func (s *Snapshot) GetResourcesAndTTL(typeURL resource.Type) map[string]types.ResourceWithTTL { if s == nil { return nil } @@ -211,7 +125,7 @@ func (s *Snapshot) GetResourcesAndTtl(typeURL string) map[string]types.ResourceW } // GetVersion returns the version for a resource type. -func (s *Snapshot) GetVersion(typeURL string) string { +func (s *Snapshot) GetVersion(typeURL resource.Type) string { if s == nil { return "" } diff --git a/pkg/cache/v3/snapshot_test.go b/pkg/cache/v3/snapshot_test.go index 9c4ed0c21f..7247641cf2 100644 --- a/pkg/cache/v3/snapshot_test.go +++ b/pkg/cache/v3/snapshot_test.go @@ -17,6 +17,8 @@ package cache_test import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/envoyproxy/go-control-plane/pkg/cache/types" "github.com/envoyproxy/go-control-plane/pkg/cache/v3" rsrc "github.com/envoyproxy/go-control-plane/pkg/resource/v3" @@ -27,18 +29,30 @@ func TestSnapshotConsistent(t *testing.T) { if err := snapshot.Consistent(); err != nil { t.Errorf("got inconsistent snapshot for %#v", snapshot) } - if snap := cache.NewSnapshot(version, []types.Resource{testEndpoint}, nil, nil, nil, nil, nil, nil); snap.Consistent() == nil { + + if snap, _ := cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + rsrc.EndpointType: {testEndpoint}, + }); snap.Consistent() == nil { t.Errorf("got consistent snapshot %#v", snap) } - if snap := cache.NewSnapshot(version, []types.Resource{resource.MakeEndpoint("missing", 8080)}, - []types.Resource{testCluster}, nil, nil, nil, nil, nil); snap.Consistent() == nil { + + if snap, _ := cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + rsrc.EndpointType: {resource.MakeEndpoint("missing", 8080)}, + rsrc.ClusterType: {testCluster}, + }); snap.Consistent() == nil { t.Errorf("got consistent snapshot %#v", snap) } - if snap := cache.NewSnapshot(version, nil, nil, nil, []types.Resource{testListener}, nil, nil, nil); snap.Consistent() == nil { + + if snap, _ := cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + rsrc.ListenerType: {testListener}}, + ); snap.Consistent() == nil { t.Errorf("got consistent snapshot %#v", snap) } - if snap := cache.NewSnapshot(version, nil, nil, - []types.Resource{resource.MakeRoute("test", clusterName)}, []types.Resource{testListener}, nil, nil, nil); snap.Consistent() == nil { + + if snap, _ := cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + rsrc.RouteType: {resource.MakeRoute("test", clusterName)}, + rsrc.ListenerType: {testListener}, + }); snap.Consistent() == nil { t.Errorf("got consistent snapshot %#v", snap) } } @@ -61,3 +75,13 @@ func TestSnapshotGetters(t *testing.T) { t.Errorf("got non-empty version for unknown type: %#v", out) } } + +func TestNewSnapshotBadType(t *testing.T) { + snap, err := cache.NewSnapshot(version, map[rsrc.Type][]types.Resource{ + "random.type": nil, + }) + + // Should receive an error from an unknown type + assert.Error(t, err) + assert.Equal(t, cache.Snapshot{}, snap) +} diff --git a/pkg/integration/ttl_integration_test.go b/pkg/integration/ttl_integration_test.go index d5ae6d5717..de42b03779 100644 --- a/pkg/integration/ttl_integration_test.go +++ b/pkg/integration/ttl_integration_test.go @@ -67,10 +67,13 @@ func TestTTLResponse(t *testing.T) { oneSecond := time.Second cla := &envoy_config_endpoint_v3.ClusterLoadAssignment{ClusterName: "resource"} - err = snapshotCache.SetSnapshot(context.Background(), "test", cache.NewSnapshotWithTtls("1", []types.ResourceWithTTL{{ - Resource: cla, - TTL: &oneSecond, - }}, nil, nil, nil, nil, nil)) + snap, _ := cache.NewSnapshotWithTTLs("1", map[resource.Type][]types.ResourceWithTTL{ + resource.EndpointType: {{ + Resource: cla, + TTL: &oneSecond, + }}, + }) + err = snapshotCache.SetSnapshot(context.Background(), "test", snap) assert.NoError(t, err) timeout := time.NewTimer(5 * time.Second) diff --git a/pkg/resource/v3/resource.go b/pkg/resource/v3/resource.go index a63c539cc4..7681848d01 100644 --- a/pkg/resource/v3/resource.go +++ b/pkg/resource/v3/resource.go @@ -8,6 +8,9 @@ import ( hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" ) +// Type is an alias to string which we expose to users of the snapshot API which accepts `resource.Type` resource URLs. +type Type = string + // Resource types in xDS v3. const ( apiTypePrefix = "type.googleapis.com/" diff --git a/pkg/test/resource/v3/resource.go b/pkg/test/resource/v3/resource.go index b793ef55d7..baaa319c18 100644 --- a/pkg/test/resource/v3/resource.go +++ b/pkg/test/resource/v3/resource.go @@ -444,16 +444,15 @@ func (ts TestSnapshot) Generate() cache.Snapshot { extensions[i] = MakeExtensionConfig(Ads, extensionConfigName, routeName) } - out := cache.NewSnapshot( - ts.Version, - endpoints, - clusters, - routes, - listeners, - runtimes, - secrets, - extensions, - ) + out, _ := cache.NewSnapshot(ts.Version, map[resource.Type][]types.Resource{ + resource.EndpointType: endpoints, + resource.ClusterType: clusters, + resource.RouteType: routes, + resource.ListenerType: listeners, + resource.RuntimeType: runtimes, + resource.SecretType: secrets, + resource.ExtensionConfigType: extensions, + }) return out }