mirror of
https://github.com/status-im/consul.git
synced 2025-02-10 20:56:46 +00:00
- A new endpoint `/v1/agent/service/:service_id` which is a generic way to look up the service for a single instance. The primary value here is that it: - **supports hash-based blocking** and so; - **replaces `/agent/connect/proxy/:proxy_id`** as the mechanism the built-in proxy uses to read its config. - It's not proxy specific and so works for any service. - It has a temporary shim to call through to the existing endpoint to preserve current managed proxy config defaulting behaviour until that is removed entirely (tested). - The built-in proxy now uses the new endpoint exclusively for it's config - The built-in proxy now has a `-sidecar-for` flag that allows the service ID of the _target_ service to be specified, on the condition that there is exactly one "sidecar" proxy (that is one that has `Proxy.DestinationServiceID` set) for the service registered. - Several fixes for edge cases for SidecarService - A fix for `Alias` checks - when running locally they didn't update their state until some external thing updated the target. If the target service has no checks registered as below, then the alias never made it past critical.
308 lines
8.1 KiB
Go
308 lines
8.1 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAgent_sidecarServiceFromNodeService(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
maxPort int
|
|
preRegister *structs.ServiceDefinition
|
|
sd *structs.ServiceDefinition
|
|
token string
|
|
autoPortsDisabled bool
|
|
wantNS *structs.NodeService
|
|
wantChecks []*structs.CheckType
|
|
wantToken string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "no sidecar",
|
|
sd: &structs.ServiceDefinition{
|
|
Name: "web",
|
|
Port: 1111,
|
|
},
|
|
token: "foo",
|
|
wantNS: nil,
|
|
wantChecks: nil,
|
|
wantToken: "",
|
|
wantErr: "", // Should NOT error
|
|
},
|
|
{
|
|
name: "all the defaults",
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantNS: &structs.NodeService{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
ID: "web1-sidecar-proxy",
|
|
Service: "web-sidecar-proxy",
|
|
Port: 2222,
|
|
LocallyRegisteredAsSidecar: true,
|
|
Proxy: structs.ConnectProxyConfig{
|
|
DestinationServiceName: "web",
|
|
DestinationServiceID: "web1",
|
|
LocalServiceAddress: "127.0.0.1",
|
|
LocalServicePort: 1111,
|
|
},
|
|
},
|
|
wantChecks: []*structs.CheckType{
|
|
&structs.CheckType{
|
|
Name: "Connect Sidecar Listening",
|
|
TCP: "127.0.0.1:2222",
|
|
Interval: 10 * time.Second,
|
|
},
|
|
&structs.CheckType{
|
|
Name: "Connect Sidecar Aliasing web1",
|
|
AliasService: "web1",
|
|
},
|
|
},
|
|
wantToken: "foo",
|
|
},
|
|
{
|
|
name: "all the allowed overrides",
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{
|
|
Name: "motorbike1",
|
|
Port: 3333,
|
|
Tags: []string{"foo", "bar"},
|
|
Address: "127.127.127.127",
|
|
Meta: map[string]string{"foo": "bar"},
|
|
Check: structs.CheckType{
|
|
ScriptArgs: []string{"sleep", "1"},
|
|
Interval: 999 * time.Second,
|
|
},
|
|
Token: "custom-token",
|
|
EnableTagOverride: true,
|
|
Proxy: &structs.ConnectProxyConfig{
|
|
DestinationServiceName: "web",
|
|
DestinationServiceID: "web1",
|
|
LocalServiceAddress: "127.0.127.0",
|
|
LocalServicePort: 9999,
|
|
Config: map[string]interface{}{"baz": "qux"},
|
|
Upstreams: structs.TestUpstreams(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantNS: &structs.NodeService{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
ID: "web1-sidecar-proxy",
|
|
Service: "motorbike1",
|
|
Port: 3333,
|
|
Tags: []string{"foo", "bar"},
|
|
Address: "127.127.127.127",
|
|
Meta: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
LocallyRegisteredAsSidecar: true,
|
|
EnableTagOverride: true,
|
|
Proxy: structs.ConnectProxyConfig{
|
|
DestinationServiceName: "web",
|
|
DestinationServiceID: "web1",
|
|
LocalServiceAddress: "127.0.127.0",
|
|
LocalServicePort: 9999,
|
|
Config: map[string]interface{}{"baz": "qux"},
|
|
Upstreams: structs.TestAddDefaultsToUpstreams(t, structs.TestUpstreams(t)),
|
|
},
|
|
},
|
|
wantChecks: []*structs.CheckType{
|
|
&structs.CheckType{
|
|
ScriptArgs: []string{"sleep", "1"},
|
|
Interval: 999 * time.Second,
|
|
},
|
|
},
|
|
wantToken: "custom-token",
|
|
},
|
|
{
|
|
name: "no auto ports available",
|
|
// register another sidecar consuming our 1 and only allocated auto port.
|
|
preRegister: &structs.ServiceDefinition{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
Name: "api-proxy-sidecar",
|
|
Port: 2222, // Consume the one available auto-port
|
|
Proxy: &structs.ConnectProxyConfig{
|
|
DestinationServiceName: "api",
|
|
},
|
|
},
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantErr: "none left in the configured range [2222, 2222]",
|
|
},
|
|
{
|
|
name: "auto ports disabled",
|
|
autoPortsDisabled: true,
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantErr: "auto-assignement disabled in config",
|
|
},
|
|
{
|
|
name: "invalid check type",
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{
|
|
Check: structs.CheckType{
|
|
TCP: "foo",
|
|
// Invalid since no interval specified
|
|
},
|
|
},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantErr: "Interval must be > 0",
|
|
},
|
|
{
|
|
name: "invalid meta",
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1111,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{
|
|
Meta: map[string]string{
|
|
"consul-reserved-key-should-be-rejected": "true",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantErr: "reserved for internal use",
|
|
},
|
|
{
|
|
name: "re-registering same sidecar with no port should pick same one",
|
|
// Allow multiple ports to be sure we get the right one
|
|
maxPort: 2500,
|
|
// Pre register the sidecar we want
|
|
preRegister: &structs.ServiceDefinition{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
ID: "web1-sidecar-proxy",
|
|
Name: "web-sidecar-proxy",
|
|
Port: 2222,
|
|
Proxy: &structs.ConnectProxyConfig{
|
|
DestinationServiceName: "web",
|
|
DestinationServiceID: "web1",
|
|
LocalServiceAddress: "127.0.0.1",
|
|
LocalServicePort: 1111,
|
|
},
|
|
},
|
|
// Register same again but with different service port
|
|
sd: &structs.ServiceDefinition{
|
|
ID: "web1",
|
|
Name: "web",
|
|
Port: 1112,
|
|
Connect: &structs.ServiceConnect{
|
|
SidecarService: &structs.ServiceDefinition{},
|
|
},
|
|
},
|
|
token: "foo",
|
|
wantNS: &structs.NodeService{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
ID: "web1-sidecar-proxy",
|
|
Service: "web-sidecar-proxy",
|
|
Port: 2222, // Should claim the same port as before
|
|
LocallyRegisteredAsSidecar: true,
|
|
Proxy: structs.ConnectProxyConfig{
|
|
DestinationServiceName: "web",
|
|
DestinationServiceID: "web1",
|
|
LocalServiceAddress: "127.0.0.1",
|
|
LocalServicePort: 1112,
|
|
},
|
|
},
|
|
wantChecks: []*structs.CheckType{
|
|
&structs.CheckType{
|
|
Name: "Connect Sidecar Listening",
|
|
TCP: "127.0.0.1:2222",
|
|
Interval: 10 * time.Second,
|
|
},
|
|
&structs.CheckType{
|
|
Name: "Connect Sidecar Aliasing web1",
|
|
AliasService: "web1",
|
|
},
|
|
},
|
|
wantToken: "foo",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Set port range to be tiny (one availabl) to test consuming all of it.
|
|
// This allows a single assigned port at 2222 thanks to being inclusive at
|
|
// both ends.
|
|
if tt.maxPort == 0 {
|
|
tt.maxPort = 2222
|
|
}
|
|
hcl := fmt.Sprintf(`
|
|
ports {
|
|
sidecar_min_port = 2222
|
|
sidecar_max_port = %d
|
|
}
|
|
`, tt.maxPort)
|
|
if tt.autoPortsDisabled {
|
|
hcl = `
|
|
ports {
|
|
sidecar_min_port = 0
|
|
sidecar_max_port = 0
|
|
}
|
|
`
|
|
}
|
|
|
|
require := require.New(t)
|
|
a := NewTestAgent("jones", hcl)
|
|
|
|
if tt.preRegister != nil {
|
|
err := a.AddService(tt.preRegister.NodeService(), nil, false, "")
|
|
require.NoError(err)
|
|
}
|
|
|
|
ns := tt.sd.NodeService()
|
|
err := ns.Validate()
|
|
require.NoError(err, "Invalid test case - NodeService must validate")
|
|
|
|
gotNS, gotChecks, gotToken, err := a.sidecarServiceFromNodeService(ns, tt.token)
|
|
if tt.wantErr != "" {
|
|
require.Error(err)
|
|
require.Contains(err.Error(), tt.wantErr)
|
|
return
|
|
}
|
|
|
|
require.NoError(err)
|
|
require.Equal(tt.wantNS, gotNS)
|
|
require.Equal(tt.wantChecks, gotChecks)
|
|
require.Equal(tt.wantToken, gotToken)
|
|
})
|
|
}
|
|
}
|