Matt Keeler 59cb12c798
Migrate the Endpoints controller to use the controller cache (#20241)
* Add cache resource decoding helpers

* Implement a common package for workload selection facilities. This includes:

   * Controller cache Index
   * ACL hooks
   * Dependency Mapper to go from workload to list of resources which select it
   * Dependency Mapper to go from a resource which selects workloads to all the workloads it selects.

* Update the endpoints controller to use the cache instead of custom mappers.

Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com>
2024-01-18 17:52:52 -05:00

136 lines
3.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package workloadselector
import (
"testing"
"github.com/hashicorp/consul/internal/controller/cache"
"github.com/hashicorp/consul/internal/controller/cache/index"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/internal/resource/resourcetest"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/consul/proto/private/prototest"
"github.com/stretchr/testify/require"
)
func TestServiceWorkloadIndexer(t *testing.T) {
c := cache.New()
i := Index[*pbcatalog.Service]("selected-workloads")
require.NoError(t, c.AddIndex(pbcatalog.ServiceType, i))
foo := rtest.Resource(pbcatalog.ServiceType, "foo").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Names: []string{
"api-2",
},
Prefixes: []string{
"api-1",
},
},
}).
WithTenancy(&pbresource.Tenancy{
Partition: "default",
Namespace: "default",
PeerName: "local",
}).
Build()
require.NoError(t, c.Insert(foo))
bar := rtest.Resource(pbcatalog.ServiceType, "bar").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Names: []string{
"api-3",
},
Prefixes: []string{
"api-2",
},
},
}).
WithTenancy(&pbresource.Tenancy{
Partition: "default",
Namespace: "default",
PeerName: "local",
}).
Build()
require.NoError(t, c.Insert(bar))
api123 := rtest.Resource(pbcatalog.WorkloadType, "api-123").
WithTenancy(&pbresource.Tenancy{
Partition: "default",
Namespace: "default",
PeerName: "local",
}).
Reference("")
api2 := rtest.Resource(pbcatalog.WorkloadType, "api-2").
WithTenancy(&pbresource.Tenancy{
Partition: "default",
Namespace: "default",
PeerName: "local",
}).
Reference("")
resources, err := c.Parents(pbcatalog.ServiceType, i.Name(), api123)
require.NoError(t, err)
require.Len(t, resources, 1)
prototest.AssertDeepEqual(t, foo, resources[0])
resources, err = c.Parents(pbcatalog.ServiceType, i.Name(), api2)
require.NoError(t, err)
require.Len(t, resources, 2)
prototest.AssertElementsMatch(t, []*pbresource.Resource{foo, bar}, resources)
refPrefix := &pbresource.Reference{
Type: pbcatalog.WorkloadType,
Tenancy: &pbresource.Tenancy{
Partition: "default",
Namespace: "default",
},
}
resources, err = c.List(pbcatalog.ServiceType, i.Name(), refPrefix, index.IndexQueryOptions{Prefix: true})
require.NoError(t, err)
// because foo and bar both have 2 index values they will appear in the output twice
require.Len(t, resources, 4)
prototest.AssertElementsMatch(t, []*pbresource.Resource{foo, bar, foo, bar}, resources)
}
func TestServiceWorkloadIndexer_FromResource_Errors(t *testing.T) {
t.Run("nil-selector", func(t *testing.T) {
res := resourcetest.MustDecode[*pbcatalog.Service](
t,
resourcetest.Resource(pbcatalog.ServiceType, "foo").
WithData(t, &pbcatalog.Service{}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build())
indexed, vals, err := fromResource(res)
require.False(t, indexed)
require.Nil(t, vals)
require.NoError(t, err)
})
t.Run("no-selections", func(t *testing.T) {
res := resourcetest.MustDecode[*pbcatalog.Service](
t,
resourcetest.Resource(pbcatalog.ServiceType, "foo").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{},
}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build())
indexed, vals, err := fromResource(res)
require.False(t, indexed)
require.Nil(t, vals)
require.NoError(t, err)
})
}