Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

180 lines
4.9 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package workloadselector
import (
"context"
"errors"
"testing"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/controller/cache/cachemock"
"github.com/hashicorp/consul/internal/controller/cache/index"
"github.com/hashicorp/consul/internal/controller/cache/index/indexmock"
"github.com/hashicorp/consul/internal/resource"
"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/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
)
var injectedError = errors.New("injected error")
func TestMapSelectorToWorkloads(t *testing.T) {
cache := cachemock.NewReadOnlyCache(t)
rt := controller.Runtime{
Cache: cache,
}
mres := indexmock.NewResourceIterator(t)
svc := resourcetest.Resource(pbcatalog.ServiceType, "api").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Prefixes: []string{"api-"},
Names: []string{"foo"},
},
}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
api1 := resourcetest.Resource(pbcatalog.WorkloadType, "api-1").
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
api2 := resourcetest.Resource(pbcatalog.WorkloadType, "api-2").
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
fooRes := resourcetest.Resource(pbcatalog.WorkloadType, "foo").
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
cache.EXPECT().
ListIterator(pbcatalog.WorkloadType, "id", &pbresource.ID{
Type: pbcatalog.WorkloadType,
Name: "api-",
Tenancy: resource.DefaultNamespacedTenancy(),
}, index.IndexQueryOptions{Prefix: true}).
Return(mres, nil).
Once()
cache.EXPECT().
Get(pbcatalog.WorkloadType, "id", &pbresource.ID{
Type: pbcatalog.WorkloadType,
Name: "foo",
Tenancy: resource.DefaultNamespacedTenancy(),
}).
Return(fooRes, nil).
Once()
mres.EXPECT().Next().Return(api1).Once()
mres.EXPECT().Next().Return(api2).Once()
mres.EXPECT().Next().Return(nil).Once()
expected := []controller.Request{
{ID: fooRes.Id},
{ID: api1.Id},
{ID: api2.Id},
}
reqs, err := MapSelectorToWorkloads[*pbcatalog.Service](context.Background(), rt, svc)
require.NoError(t, err)
prototest.AssertElementsMatch(t, expected, reqs)
}
func TestMapSelectorToWorkloads_DecodeError(t *testing.T) {
res := resourcetest.Resource(pbcatalog.ServiceType, "foo").
WithData(t, &pbcatalog.DNSPolicy{}).
Build()
reqs, err := MapSelectorToWorkloads[*pbcatalog.Service](context.Background(), controller.Runtime{}, res)
require.Nil(t, reqs)
require.Error(t, err)
require.ErrorAs(t, err, &resource.ErrDataParse{})
}
func TestMapSelectorToWorkloads_CacheError(t *testing.T) {
cache := cachemock.NewReadOnlyCache(t)
rt := controller.Runtime{
Cache: cache,
}
svc := resourcetest.Resource(pbcatalog.ServiceType, "api").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Prefixes: []string{"api-"},
},
}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
cache.EXPECT().
ListIterator(pbcatalog.WorkloadType, "id", &pbresource.ID{
Type: pbcatalog.WorkloadType,
Name: "api-",
Tenancy: resource.DefaultNamespacedTenancy(),
}, index.IndexQueryOptions{Prefix: true}).
Return(nil, injectedError).
Once()
reqs, err := MapSelectorToWorkloads[*pbcatalog.Service](context.Background(), rt, svc)
require.ErrorIs(t, err, injectedError)
require.Nil(t, reqs)
}
func TestMapWorkloadsToSelectors(t *testing.T) {
cache := cachemock.NewReadOnlyCache(t)
rt := controller.Runtime{
Cache: cache,
Logger: hclog.NewNullLogger(),
}
dm := MapWorkloadsToSelectors(pbcatalog.ServiceType, "selected-workloads")
workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-123").
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
svc1 := resourcetest.Resource(pbcatalog.ServiceType, "foo").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Prefixes: []string{"api-"},
},
}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
svc2 := resourcetest.Resource(pbcatalog.ServiceType, "bar").
WithData(t, &pbcatalog.Service{
Workloads: &pbcatalog.WorkloadSelector{
Prefixes: []string{"api-"},
},
}).
WithTenancy(resource.DefaultNamespacedTenancy()).
Build()
mres := indexmock.NewResourceIterator(t)
cache.EXPECT().
ParentsIterator(pbcatalog.ServiceType, "selected-workloads", workload.Id).
Return(mres, nil).
Once()
mres.EXPECT().Next().Return(svc1).Once()
mres.EXPECT().Next().Return(svc2).Once()
mres.EXPECT().Next().Return(nil).Once()
reqs, err := dm(context.Background(), rt, workload)
require.NoError(t, err)
expected := []controller.Request{
{ID: svc1.Id},
{ID: svc2.Id},
}
prototest.AssertElementsMatch(t, expected, reqs)
}