open-consul/connect/resolver_test.go
Paul Banks 92fe8c8e89 Add Proxy Upstreams to Service Definition (#4639)
* Refactor Service Definition ProxyDestination.

This includes:
 - Refactoring all internal structs used
 - Updated tests for both deprecated and new input for:
   - Agent Services endpoint response
   - Agent Service endpoint response
   - Agent Register endpoint
     - Unmanaged deprecated field
     - Unmanaged new fields
     - Managed deprecated upstreams
     - Managed new
   - Catalog Register
     - Unmanaged deprecated field
     - Unmanaged new fields
     - Managed deprecated upstreams
     - Managed new
   - Catalog Services endpoint response
   - Catalog Node endpoint response
   - Catalog Service endpoint response
 - Updated API tests for all of the above too (both deprecated and new forms of register)

TODO:
 - config package changes for on-disk service definitions
 - proxy config endpoint
 - built-in proxy support for new fields

* Agent proxy config endpoint updated with upstreams

* Config file changes for upstreams.

* Add upstream opaque config and update all tests to ensure it works everywhere.

* Built in proxy working with new Upstreams config

* Command fixes and deprecations

* Fix key translation, upstream type defaults and a spate of other subtele bugs found with ned to end test scripts...

TODO: tests still failing on one case that needs a fix. I think it's key translation for upstreams nested in Managed proxy struct.

* Fix translated keys in API registration.
≈

* Fixes from docs
 - omit some empty undocumented fields in API
 - Bring back ServiceProxyDestination in Catalog responses to not break backwards compat - this was removed assuming it was only used internally.

* Documentation updates for Upstreams in service definition

* Fixes for tests broken by many refactors.

* Enable travis on f-connect branch in this branch too.

* Add consistent Deprecation comments to ProxyDestination uses

* Update version number on deprecation notices, and correct upstream datacenter field with explanation in docs
2018-10-10 16:55:34 +01:00

328 lines
7.3 KiB
Go

package connect
import (
"context"
"testing"
"time"
"github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/api"
"github.com/stretchr/testify/require"
)
func TestStaticResolver_Resolve(t *testing.T) {
type fields struct {
Addr string
CertURI connect.CertURI
}
tests := []struct {
name string
fields fields
}{
{
name: "simples",
fields: fields{"1.2.3.4:80", connect.TestSpiffeIDService(t, "foo")},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sr := StaticResolver{
Addr: tt.fields.Addr,
CertURI: tt.fields.CertURI,
}
addr, certURI, err := sr.Resolve(context.Background())
require := require.New(t)
require.Nil(err)
require.Equal(sr.Addr, addr)
require.Equal(sr.CertURI, certURI)
})
}
}
func TestConsulResolver_Resolve(t *testing.T) {
// Setup a local test agent to query
agent := agent.NewTestAgent("test-consul", "")
defer agent.Shutdown()
cfg := api.DefaultConfig()
cfg.Address = agent.HTTPAddr()
client, err := api.NewClient(cfg)
require.Nil(t, err)
// Setup a service with a connect proxy instance
regSrv := &api.AgentServiceRegistration{
Name: "web",
Port: 8080,
}
err = client.Agent().ServiceRegister(regSrv)
require.Nil(t, err)
regProxy := &api.AgentServiceRegistration{
Kind: "connect-proxy",
Name: "web-proxy",
Port: 9090,
Proxy: &api.AgentServiceConnectProxyConfig{
DestinationServiceName: "web",
},
}
err = client.Agent().ServiceRegister(regProxy)
require.Nil(t, err)
// And another proxy so we can test handling with multiple endpoints returned
regProxy.Port = 9091
regProxy.ID = "web-proxy-2"
err = client.Agent().ServiceRegister(regProxy)
require.Nil(t, err)
// Add a native service
{
regSrv := &api.AgentServiceRegistration{
Name: "db",
Port: 8080,
Connect: &api.AgentServiceConnect{
Native: true,
},
}
require.NoError(t, client.Agent().ServiceRegister(regSrv))
}
// Add a prepared query
queryId, _, err := client.PreparedQuery().Create(&api.PreparedQueryDefinition{
Name: "test-query",
Service: api.ServiceQuery{
Service: "web",
Connect: true,
},
}, nil)
require.NoError(t, err)
proxyAddrs := []string{
agent.Config.AdvertiseAddrLAN.String() + ":9090",
agent.Config.AdvertiseAddrLAN.String() + ":9091",
}
type fields struct {
Namespace string
Name string
Type int
Datacenter string
}
tests := []struct {
name string
fields fields
timeout time.Duration
wantAddr string
wantCertURI connect.CertURI
wantErr bool
addrs []string
}{
{
name: "basic service discovery",
fields: fields{
Namespace: "default",
Name: "web",
Type: ConsulResolverTypeService,
},
wantCertURI: connect.TestSpiffeIDService(t, "web"),
wantErr: false,
addrs: proxyAddrs,
},
{
name: "basic service with native service",
fields: fields{
Namespace: "default",
Name: "db",
Type: ConsulResolverTypeService,
},
wantCertURI: connect.TestSpiffeIDService(t, "db"),
wantErr: false,
},
{
name: "Bad Type errors",
fields: fields{
Namespace: "default",
Name: "web",
Type: 123,
},
wantErr: true,
},
{
name: "Non-existent service errors",
fields: fields{
Namespace: "default",
Name: "foo",
Type: ConsulResolverTypeService,
},
wantErr: true,
},
{
name: "timeout errors",
fields: fields{
Namespace: "default",
Name: "web",
Type: ConsulResolverTypeService,
},
timeout: 1 * time.Nanosecond,
wantErr: true,
},
{
name: "prepared query by id",
fields: fields{
Name: queryId,
Type: ConsulResolverTypePreparedQuery,
},
wantCertURI: connect.TestSpiffeIDService(t, "web"),
wantErr: false,
addrs: proxyAddrs,
},
{
name: "prepared query by name",
fields: fields{
Name: "test-query",
Type: ConsulResolverTypePreparedQuery,
},
wantCertURI: connect.TestSpiffeIDService(t, "web"),
wantErr: false,
addrs: proxyAddrs,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
cr := &ConsulResolver{
Client: client,
Namespace: tt.fields.Namespace,
Name: tt.fields.Name,
Type: tt.fields.Type,
Datacenter: tt.fields.Datacenter,
}
// WithCancel just to have a cancel func in scope to assign in the if
// clause.
ctx, cancel := context.WithCancel(context.Background())
if tt.timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, tt.timeout)
}
defer cancel()
gotAddr, gotCertURI, err := cr.Resolve(ctx)
if tt.wantErr {
require.NotNil(err)
return
}
require.Nil(err)
require.Equal(tt.wantCertURI, gotCertURI)
if len(tt.addrs) > 0 {
require.Contains(tt.addrs, gotAddr)
}
})
}
}
func TestConsulResolverFromAddrFunc(t *testing.T) {
// Don't need an actual instance since we don't do the service discovery but
// we do want to assert the client is pass through correctly.
client, err := api.NewClient(api.DefaultConfig())
require.NoError(t, err)
tests := []struct {
name string
addr string
want Resolver
wantErr string
}{
{
name: "service",
addr: "foo.service.consul",
want: &ConsulResolver{
Client: client,
Namespace: "default",
Name: "foo",
Type: ConsulResolverTypeService,
},
},
{
name: "query",
addr: "foo.query.consul",
want: &ConsulResolver{
Client: client,
Namespace: "default",
Name: "foo",
Type: ConsulResolverTypePreparedQuery,
},
},
{
name: "service with dc",
addr: "foo.service.dc2.consul",
want: &ConsulResolver{
Client: client,
Datacenter: "dc2",
Namespace: "default",
Name: "foo",
Type: ConsulResolverTypeService,
},
},
{
name: "query with dc",
addr: "foo.query.dc2.consul",
want: &ConsulResolver{
Client: client,
Datacenter: "dc2",
Namespace: "default",
Name: "foo",
Type: ConsulResolverTypePreparedQuery,
},
},
{
name: "invalid host:port",
addr: "%%%",
wantErr: "invalid Consul DNS domain",
},
{
name: "custom domain",
addr: "foo.service.my-consul.com",
wantErr: "invalid Consul DNS domain",
},
{
name: "unsupported query type",
addr: "foo.connect.consul",
wantErr: "unsupported Consul DNS domain",
},
{
name: "unsupported query type and datacenter",
addr: "foo.connect.dc1.consul",
wantErr: "unsupported Consul DNS domain",
},
{
name: "unsupported query type and datacenter",
addr: "foo.connect.dc1.consul",
wantErr: "unsupported Consul DNS domain",
},
{
name: "unsupported tag filter",
addr: "tag1.foo.service.consul",
wantErr: "unsupported Consul DNS domain",
},
{
name: "unsupported tag filter with DC",
addr: "tag1.foo.service.dc1.consul",
wantErr: "unsupported Consul DNS domain",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
fn := ConsulResolverFromAddrFunc(client)
got, gotErr := fn(tt.addr)
if tt.wantErr != "" {
require.Error(gotErr)
require.Contains(gotErr.Error(), tt.wantErr)
} else {
require.NoError(gotErr)
require.Equal(tt.want, got)
}
})
}
}