mirror of https://github.com/hashicorp/consul
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.
859 lines
24 KiB
859 lines
24 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: BUSL-1.1 |
|
|
|
package discovery |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"testing" |
|
|
|
"github.com/stretchr/testify/mock" |
|
"github.com/stretchr/testify/require" |
|
"google.golang.org/grpc/codes" |
|
"google.golang.org/grpc/status" |
|
"google.golang.org/protobuf/types/known/anypb" |
|
|
|
"github.com/hashicorp/consul/agent/config" |
|
mockpbresource "github.com/hashicorp/consul/grpcmocks/proto-public/pbresource" |
|
"github.com/hashicorp/consul/internal/resource" |
|
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" |
|
"github.com/hashicorp/consul/proto-public/pbresource" |
|
"github.com/hashicorp/consul/sdk/testutil" |
|
) |
|
|
|
var ( |
|
unknownErr = errors.New("I don't feel so good") |
|
) |
|
|
|
// Test_FetchService tests the FetchService method in scenarios where the RPC |
|
// call succeeds and fails. |
|
func Test_FetchWorkload(t *testing.T) { |
|
|
|
rc := &config.RuntimeConfig{ |
|
DNSOnlyPassing: false, |
|
} |
|
|
|
tests := []struct { |
|
name string |
|
queryPayload *QueryPayload |
|
context Context |
|
configureMockClient func(mockClient *mockpbresource.ResourceServiceClient_Expecter) |
|
expectedResult *Result |
|
expectedErr error |
|
}{ |
|
{ |
|
name: "FetchWorkload returns result", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
result := getTestWorkloadResponse(t, "foo-1234", "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: &Result{ |
|
Node: &Location{Name: "foo-1234", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
}, |
|
expectedErr: nil, |
|
}, |
|
{ |
|
name: "FetchWorkload for non-existent workload", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
input := getTestWorkloadResponse(t, "foo-1234", "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(nil, status.Error(codes.NotFound, "not found")). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, input.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: nil, |
|
expectedErr: ErrNotFound, |
|
}, |
|
{ |
|
name: "FetchWorkload encounters a resource client error", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
input := getTestWorkloadResponse(t, "foo-1234", "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(nil, unknownErr). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, input.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: nil, |
|
expectedErr: unknownErr, |
|
}, |
|
{ |
|
name: "FetchWorkload with a matching port", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
PortName: "api", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
result := getTestWorkloadResponse(t, "foo-1234", "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: &Result{ |
|
Node: &Location{Name: "foo-1234", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
}, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
}, |
|
expectedErr: nil, |
|
}, |
|
{ |
|
name: "FetchWorkload with a matching port", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
PortName: "not-api", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
result := getTestWorkloadResponse(t, "foo-1234", "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: nil, |
|
expectedErr: ErrNotFound, |
|
}, |
|
{ |
|
name: "FetchWorkload returns result for non-default tenancy", |
|
queryPayload: &QueryPayload{ |
|
Name: "foo-1234", |
|
Tenancy: QueryTenancy{ |
|
Namespace: "test-namespace", |
|
Partition: "test-partition", |
|
}, |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
result := getTestWorkloadResponse(t, "foo-1234", "test-namespace", "test-partition") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
require.Equal(t, result.GetResource().GetId().GetTenancy().GetNamespace(), req.Id.Tenancy.Namespace) |
|
require.Equal(t, result.GetResource().GetId().GetTenancy().GetPartition(), req.Id.Tenancy.Partition) |
|
}) |
|
}, |
|
expectedResult: &Result{ |
|
Node: &Location{Name: "foo-1234", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
Tenancy: ResultTenancy{ |
|
Namespace: "test-namespace", |
|
Partition: "test-partition", |
|
}, |
|
}, |
|
expectedErr: nil, |
|
}, |
|
} |
|
|
|
for _, tc := range tests { |
|
t.Run(tc.name, func(t *testing.T) { |
|
logger := testutil.Logger(t) |
|
|
|
client := mockpbresource.NewResourceServiceClient(t) |
|
mockClient := client.EXPECT() |
|
tc.configureMockClient(mockClient) |
|
|
|
df := NewV2DataFetcher(rc, client, logger) |
|
|
|
result, err := df.FetchWorkload(tc.context, tc.queryPayload) |
|
require.True(t, errors.Is(err, tc.expectedErr)) |
|
require.Equal(t, tc.expectedResult, result) |
|
}) |
|
} |
|
} |
|
|
|
// Test_V2FetchEndpoints the FetchService method in scenarios where the RPC |
|
// call succeeds and fails. |
|
func Test_V2FetchEndpoints(t *testing.T) { |
|
|
|
tests := []struct { |
|
name string |
|
queryPayload *QueryPayload |
|
context Context |
|
configureMockClient func(mockClient *mockpbresource.ResourceServiceClient_Expecter) |
|
rc *config.RuntimeConfig |
|
expectedResult []*Result |
|
expectedErr error |
|
verifyShuffle bool |
|
}{ |
|
{ |
|
name: "FetchEndpoints returns result", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
endpoints := []*pbcatalog.Endpoint{ |
|
makeEndpoint("consul-1", "1.2.3.4", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
serviceEndpoints := getTestEndpointsResponse(t, "", "", endpoints...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(serviceEndpoints, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, serviceEndpoints.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 1, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns empty result with no endpoints", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
|
|
result := getTestEndpointsResponse(t, "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: []*Result{}, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns a name error when the ServiceEndpoint does not exist", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
|
|
result := getTestEndpointsResponse(t, "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(nil, status.Error(codes.NotFound, "not found")). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedErr: ErrNotFound, |
|
}, |
|
{ |
|
name: "FetchEndpoints encounters a resource client error", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
|
|
result := getTestEndpointsResponse(t, "", "") |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(nil, unknownErr). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedErr: unknownErr, |
|
}, |
|
{ |
|
name: "FetchEndpoints always filters out critical endpoints; DNS weights applied correctly", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
results := []*pbcatalog.Endpoint{ |
|
makeEndpoint("consul-1", "1.2.3.4", pbcatalog.Health_HEALTH_PASSING, 2, 3), |
|
makeEndpoint("consul-2", "2.3.4.5", pbcatalog.Health_HEALTH_WARNING, 2, 3), |
|
makeEndpoint("consul-3", "3.4.5.6", pbcatalog.Health_HEALTH_CRITICAL, 2, 3), |
|
} |
|
|
|
result := getTestEndpointsResponse(t, "", "", results...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 2, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
}, |
|
{ |
|
Node: &Location{Name: "consul-2", Address: "2.3.4.5"}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 3, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints filters out warning endpoints when DNSOnlyPassing is true", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
results := []*pbcatalog.Endpoint{ |
|
makeEndpoint("consul-1", "1.2.3.4", pbcatalog.Health_HEALTH_PASSING, 2, 3), |
|
makeEndpoint("consul-2", "2.3.4.5", pbcatalog.Health_HEALTH_WARNING, 2, 3), |
|
makeEndpoint("consul-3", "3.4.5.6", pbcatalog.Health_HEALTH_CRITICAL, 2, 3), |
|
} |
|
|
|
result := getTestEndpointsResponse(t, "", "", results...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
rc: &config.RuntimeConfig{ |
|
DNSOnlyPassing: true, |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "1.2.3.4"}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 2, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints shuffles the results", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
results := []*pbcatalog.Endpoint{ |
|
// use a set of 10 elements, the odds of getting the same result are 1 in 3628800 |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-2", "10.0.0.2", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-3", "10.0.0.3", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-4", "10.0.0.4", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-5", "10.0.0.5", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-6", "10.0.0.6", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-7", "10.0.0.7", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-8", "10.0.0.8", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-9", "10.0.0.9", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-10", "10.0.0.10", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
result := getTestEndpointsResponse(t, "", "", results...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: func() []*Result { |
|
results := make([]*Result, 0, 10) |
|
|
|
for i := 0; i < 10; i++ { |
|
name := fmt.Sprintf("consul-%d", i+1) |
|
address := fmt.Sprintf("10.0.0.%d", i+1) |
|
result := &Result{ |
|
Node: &Location{Name: name, Address: address}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 1, |
|
}, |
|
} |
|
results = append(results, result) |
|
} |
|
return results |
|
}(), |
|
verifyShuffle: true, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns only the specified limit", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
Limit: 1, |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
results := []*pbcatalog.Endpoint{ |
|
// intentionally all the same to make this easier to verify |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
result := getTestEndpointsResponse(t, "", "", results...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "10.0.0.1"}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 1, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns results with non-default tenancy", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
Tenancy: QueryTenancy{ |
|
Namespace: "test-namespace", |
|
Partition: "test-partition", |
|
}, |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
results := []*pbcatalog.Endpoint{ |
|
// intentionally all the same to make this easier to verify |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
result := getTestEndpointsResponse(t, "test-namespace", "test-partition", results...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(result, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, result.GetResource().GetId().GetName(), req.Id.Name) |
|
require.Equal(t, result.GetResource().GetId().GetTenancy().GetNamespace(), req.Id.Tenancy.Namespace) |
|
require.Equal(t, result.GetResource().GetId().GetTenancy().GetPartition(), req.Id.Tenancy.Partition) |
|
}) |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "10.0.0.1"}, |
|
Type: ResultTypeWorkload, |
|
Tenancy: ResultTenancy{ |
|
Namespace: "test-namespace", |
|
Partition: "test-partition", |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 1, |
|
}, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
{ |
|
Name: "mesh", |
|
Number: 21000, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns only a specific port if is one requested", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
PortName: "api", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
endpoints := []*pbcatalog.Endpoint{ |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
serviceEndpoints := getTestEndpointsResponse(t, "", "", endpoints...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(serviceEndpoints, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, serviceEndpoints.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedResult: []*Result{ |
|
{ |
|
Node: &Location{Name: "consul-1", Address: "10.0.0.1"}, |
|
Type: ResultTypeWorkload, |
|
Ports: []Port{ |
|
{ |
|
Name: "api", |
|
Number: 5678, |
|
}, |
|
// No mesh port this time |
|
}, |
|
Tenancy: ResultTenancy{ |
|
Namespace: resource.DefaultNamespaceName, |
|
Partition: resource.DefaultPartitionName, |
|
}, |
|
DNS: DNSConfig{ |
|
Weight: 1, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "FetchEndpoints returns a name error when a service doesn't implement the requested port", |
|
queryPayload: &QueryPayload{ |
|
Name: "consul", |
|
PortName: "banana", |
|
}, |
|
context: Context{ |
|
Token: "test-token", |
|
}, |
|
configureMockClient: func(mockClient *mockpbresource.ResourceServiceClient_Expecter) { |
|
endpoints := []*pbcatalog.Endpoint{ |
|
makeEndpoint("consul-1", "10.0.0.1", pbcatalog.Health_HEALTH_PASSING, 0, 0), |
|
} |
|
|
|
serviceEndpoints := getTestEndpointsResponse(t, "", "", endpoints...) |
|
mockClient.Read(mock.Anything, mock.Anything). |
|
Return(serviceEndpoints, nil). |
|
Once(). |
|
Run(func(args mock.Arguments) { |
|
req := args.Get(1).(*pbresource.ReadRequest) |
|
require.Equal(t, serviceEndpoints.GetResource().GetId().GetName(), req.Id.Name) |
|
}) |
|
}, |
|
expectedErr: ErrNotFound, |
|
}, |
|
} |
|
|
|
for _, tc := range tests { |
|
t.Run(tc.name, func(t *testing.T) { |
|
logger := testutil.Logger(t) |
|
|
|
client := mockpbresource.NewResourceServiceClient(t) |
|
mockClient := client.EXPECT() |
|
tc.configureMockClient(mockClient) |
|
|
|
if tc.rc == nil { |
|
tc.rc = &config.RuntimeConfig{ |
|
DNSOnlyPassing: false, |
|
} |
|
} |
|
|
|
df := NewV2DataFetcher(tc.rc, client, logger) |
|
|
|
result, err := df.FetchEndpoints(tc.context, tc.queryPayload, LookupTypeService) |
|
require.True(t, errors.Is(err, tc.expectedErr)) |
|
|
|
if tc.verifyShuffle { |
|
require.NotEqualf(t, tc.expectedResult, result, "expected result to be shuffled. There is a small probability that it shuffled back to the original order. In that case, you may want to play the lottery.") |
|
} |
|
|
|
require.ElementsMatchf(t, tc.expectedResult, result, "elements of results should match") |
|
}) |
|
} |
|
} |
|
|
|
func getTestWorkloadResponse(t *testing.T, name string, nsOverride string, partitionOverride string) *pbresource.ReadResponse { |
|
workload := &pbcatalog.Workload{ |
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
{ |
|
Host: "1.2.3.4", |
|
Ports: []string{"api", "mesh"}, |
|
}, |
|
}, |
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
"api": { |
|
Port: 5678, |
|
}, |
|
"mesh": { |
|
Port: 21000, |
|
}, |
|
}, |
|
Identity: "test-identity", |
|
} |
|
|
|
data, err := anypb.New(workload) |
|
require.NoError(t, err) |
|
|
|
resp := &pbresource.ReadResponse{ |
|
Resource: &pbresource.Resource{ |
|
Id: &pbresource.ID{ |
|
Name: name, |
|
Type: pbcatalog.WorkloadType, |
|
Tenancy: resource.DefaultNamespacedTenancy(), |
|
}, |
|
Data: data, |
|
}, |
|
} |
|
|
|
if nsOverride != "" { |
|
resp.Resource.Id.Tenancy.Namespace = nsOverride |
|
} |
|
if partitionOverride != "" { |
|
resp.Resource.Id.Tenancy.Partition = partitionOverride |
|
} |
|
|
|
return resp |
|
} |
|
|
|
func makeEndpoint(name string, address string, health pbcatalog.Health, weightPassing, weightWarning uint32) *pbcatalog.Endpoint { |
|
endpoint := &pbcatalog.Endpoint{ |
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
{ |
|
Host: address, |
|
Ports: []string{"api"}, |
|
}, |
|
}, |
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
"api": { |
|
Port: 5678, |
|
}, |
|
"mesh": { |
|
Port: 21000, |
|
}, |
|
}, |
|
HealthStatus: health, |
|
TargetRef: &pbresource.ID{ |
|
Name: name, |
|
}, |
|
} |
|
|
|
if weightPassing > 0 || weightWarning > 0 { |
|
endpoint.Dns = &pbcatalog.DNSPolicy{ |
|
Weights: &pbcatalog.Weights{ |
|
Passing: weightPassing, |
|
Warning: weightWarning, |
|
}, |
|
} |
|
} |
|
|
|
return endpoint |
|
} |
|
|
|
func getTestEndpointsResponse(t *testing.T, nsOverride string, partitionOverride string, endpoints ...*pbcatalog.Endpoint) *pbresource.ReadResponse { |
|
serviceEndpoints := &pbcatalog.ServiceEndpoints{ |
|
Endpoints: endpoints, |
|
} |
|
|
|
data, err := anypb.New(serviceEndpoints) |
|
require.NoError(t, err) |
|
|
|
resp := &pbresource.ReadResponse{ |
|
Resource: &pbresource.Resource{ |
|
Id: &pbresource.ID{ |
|
Name: "consul", |
|
Type: pbcatalog.ServiceType, |
|
Tenancy: resource.DefaultNamespacedTenancy(), |
|
}, |
|
Data: data, |
|
}, |
|
} |
|
|
|
if nsOverride != "" { |
|
resp.Resource.Id.Tenancy.Namespace = nsOverride |
|
} |
|
if partitionOverride != "" { |
|
resp.Resource.Id.Tenancy.Partition = partitionOverride |
|
} |
|
|
|
return resp |
|
}
|
|
|