2019-03-22 19:37:14 +00:00
|
|
|
package xds
|
|
|
|
|
|
|
|
import (
|
2019-04-29 16:27:57 +00:00
|
|
|
"bytes"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path"
|
2019-03-22 19:37:14 +00:00
|
|
|
"testing"
|
2019-04-29 16:27:57 +00:00
|
|
|
"text/template"
|
2019-03-22 19:37:14 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/proxycfg"
|
2019-04-29 16:27:57 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2019-03-22 19:37:14 +00:00
|
|
|
)
|
|
|
|
|
2019-04-29 16:27:57 +00:00
|
|
|
func TestClustersFromSnapshot(t *testing.T) {
|
|
|
|
|
2019-03-22 19:37:14 +00:00
|
|
|
tests := []struct {
|
2019-04-29 16:27:57 +00:00
|
|
|
name string
|
|
|
|
// Setup is called before the test starts. It is passed the snapshot from
|
|
|
|
// TestConfigSnapshot and is allowed to modify it in any way to setup the
|
|
|
|
// test input.
|
|
|
|
setup func(snap *proxycfg.ConfigSnapshot)
|
|
|
|
overrideGoldenName string
|
2019-03-22 19:37:14 +00:00
|
|
|
}{
|
|
|
|
{
|
2019-04-29 16:27:57 +00:00
|
|
|
name: "defaults",
|
|
|
|
setup: nil, // Default snapshot
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "custom-local-app",
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Config["envoy_local_cluster_json"] =
|
|
|
|
customAppClusterJSON(t, customClusterJSONOptions{
|
|
|
|
Name: "mylocal",
|
|
|
|
IncludeType: false,
|
|
|
|
})
|
2019-03-22 19:37:14 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2019-04-29 16:27:57 +00:00
|
|
|
name: "custom-local-app-typed",
|
|
|
|
overrideGoldenName: "custom-local-app",
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Config["envoy_local_cluster_json"] =
|
|
|
|
customAppClusterJSON(t, customClusterJSONOptions{
|
|
|
|
Name: "mylocal",
|
|
|
|
IncludeType: true,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "custom-upstream",
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
|
|
|
customAppClusterJSON(t, customClusterJSONOptions{
|
|
|
|
Name: "myservice",
|
|
|
|
IncludeType: false,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "custom-upstream-typed",
|
|
|
|
overrideGoldenName: "custom-upstream",
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
|
|
|
customAppClusterJSON(t, customClusterJSONOptions{
|
|
|
|
Name: "myservice",
|
|
|
|
IncludeType: true,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "custom-upstream-ignores-tls",
|
|
|
|
overrideGoldenName: "custom-upstream", // should be the same
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
|
|
|
customAppClusterJSON(t, customClusterJSONOptions{
|
|
|
|
Name: "myservice",
|
|
|
|
IncludeType: true,
|
|
|
|
// Attempt to override the TLS context should be ignored
|
|
|
|
TLSContext: `{"commonTlsContext": {}}`,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "custom-timeouts",
|
|
|
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
|
|
|
snap.Proxy.Config["local_connect_timeout_ms"] = 1234
|
|
|
|
snap.Proxy.Upstreams[0].Config["connect_timeout_ms"] = 2345
|
2019-03-22 19:37:14 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2019-04-29 16:27:57 +00:00
|
|
|
|
2019-03-22 19:37:14 +00:00
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
require := require.New(t)
|
2019-04-29 16:27:57 +00:00
|
|
|
|
|
|
|
// Sanity check default with no overrides first
|
|
|
|
snap := proxycfg.TestConfigSnapshot(t)
|
|
|
|
|
|
|
|
// We need to replace the TLS certs with deterministic ones to make golden
|
|
|
|
// files workable. Note we don't update these otherwise they'd change
|
|
|
|
// golder files for every test case and so not be any use!
|
|
|
|
snap.Leaf.CertPEM = golden(t, "test-leaf-cert", "")
|
|
|
|
snap.Leaf.PrivateKeyPEM = golden(t, "test-leaf-key", "")
|
|
|
|
snap.Roots.Roots[0].RootCert = golden(t, "test-root-cert", "")
|
|
|
|
|
|
|
|
if tt.setup != nil {
|
|
|
|
tt.setup(snap)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need server just for logger dependency
|
|
|
|
s := Server{Logger: log.New(os.Stderr, "", log.LstdFlags)}
|
|
|
|
|
|
|
|
clusters, err := s.clustersFromSnapshot(snap, "my-token")
|
2019-03-22 19:37:14 +00:00
|
|
|
require.NoError(err)
|
2019-04-29 16:27:57 +00:00
|
|
|
r, err := createResponse(ClusterType, "00000001", "00000001", clusters)
|
|
|
|
require.NoError(err)
|
|
|
|
|
|
|
|
gotJSON := responseToJSON(t, r)
|
|
|
|
|
|
|
|
gName := tt.name
|
|
|
|
if tt.overrideGoldenName != "" {
|
|
|
|
gName = tt.overrideGoldenName
|
|
|
|
}
|
2019-03-22 19:37:14 +00:00
|
|
|
|
2019-04-29 16:27:57 +00:00
|
|
|
require.JSONEq(golden(t, path.Join("clusters", gName), gotJSON), gotJSON)
|
2019-03-22 19:37:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-04-29 16:27:57 +00:00
|
|
|
|
|
|
|
func expectClustersJSONResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) map[string]string {
|
|
|
|
return map[string]string{
|
|
|
|
"local_app": `
|
|
|
|
{
|
|
|
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
"name": "local_app",
|
2019-06-07 12:10:43 +00:00
|
|
|
"type": "STATIC",
|
2019-04-29 16:27:57 +00:00
|
|
|
"connectTimeout": "5s",
|
|
|
|
"loadAssignment": {
|
|
|
|
"clusterName": "local_app",
|
|
|
|
"endpoints": [
|
|
|
|
{
|
|
|
|
"lbEndpoints": [
|
|
|
|
{
|
|
|
|
"endpoint": {
|
|
|
|
"address": {
|
|
|
|
"socketAddress": {
|
|
|
|
"address": "127.0.0.1",
|
|
|
|
"portValue": 8080
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}`,
|
|
|
|
"db": `
|
|
|
|
{
|
|
|
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
"name": "db",
|
|
|
|
"type": "EDS",
|
|
|
|
"edsClusterConfig": {
|
|
|
|
"edsConfig": {
|
|
|
|
"ads": {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"outlierDetection": {
|
|
|
|
|
|
|
|
},
|
|
|
|
"connectTimeout": "1s",
|
|
|
|
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
|
|
|
}`,
|
|
|
|
"prepared_query:geo-cache": `
|
|
|
|
{
|
|
|
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
"name": "prepared_query:geo-cache",
|
|
|
|
"type": "EDS",
|
|
|
|
"edsClusterConfig": {
|
|
|
|
"edsConfig": {
|
|
|
|
"ads": {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"outlierDetection": {
|
|
|
|
|
|
|
|
},
|
|
|
|
"connectTimeout": "5s",
|
|
|
|
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
|
|
|
}`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectClustersJSONFromResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64, resourcesJSON map[string]string) string {
|
|
|
|
resJSON := ""
|
|
|
|
|
|
|
|
// Sort resources into specific order because that matters in JSONEq
|
|
|
|
// comparison later.
|
|
|
|
keyOrder := []string{"local_app"}
|
|
|
|
for _, u := range snap.Proxy.Upstreams {
|
|
|
|
keyOrder = append(keyOrder, u.Identifier())
|
|
|
|
}
|
|
|
|
for _, k := range keyOrder {
|
|
|
|
j, ok := resourcesJSON[k]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if resJSON != "" {
|
|
|
|
resJSON += ",\n"
|
|
|
|
}
|
|
|
|
resJSON += j
|
|
|
|
}
|
|
|
|
|
|
|
|
return `{
|
|
|
|
"versionInfo": "` + hexString(v) + `",
|
|
|
|
"resources": [` + resJSON + `],
|
|
|
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
"nonce": "` + hexString(n) + `"
|
|
|
|
}`
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectClustersJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
|
|
|
return expectClustersJSONFromResources(t, snap, token, v, n,
|
|
|
|
expectClustersJSONResources(t, snap, token, v, n))
|
|
|
|
}
|
|
|
|
|
|
|
|
type customClusterJSONOptions struct {
|
|
|
|
Name string
|
|
|
|
IncludeType bool
|
|
|
|
TLSContext string
|
|
|
|
}
|
|
|
|
|
|
|
|
var customEDSClusterJSONTpl = `{
|
|
|
|
{{ if .IncludeType -}}
|
|
|
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
{{- end }}
|
|
|
|
{{ if .TLSContext -}}
|
|
|
|
"tlsContext": {{ .TLSContext }},
|
|
|
|
{{- end }}
|
|
|
|
"name": "{{ .Name }}",
|
|
|
|
"type": "EDS",
|
|
|
|
"edsClusterConfig": {
|
|
|
|
"edsConfig": {
|
|
|
|
"ads": {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"connectTimeout": "5s"
|
|
|
|
}`
|
|
|
|
|
|
|
|
var customEDSClusterJSONTemplate = template.Must(template.New("").Parse(customEDSClusterJSONTpl))
|
|
|
|
|
|
|
|
func customEDSClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
|
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := customEDSClusterJSONTemplate.Execute(&buf, opts)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
var customAppClusterJSONTpl = `{
|
|
|
|
{{ if .IncludeType -}}
|
|
|
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
|
|
{{- end }}
|
|
|
|
{{ if .TLSContext -}}
|
|
|
|
"tlsContext": {{ .TLSContext }},
|
|
|
|
{{- end }}
|
|
|
|
"name": "{{ .Name }}",
|
|
|
|
"connectTimeout": "5s",
|
|
|
|
"hosts": [
|
|
|
|
{
|
|
|
|
"socketAddress": {
|
|
|
|
"address": "127.0.0.1",
|
|
|
|
"portValue": 8080
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
var customAppClusterJSONTemplate = template.Must(template.New("").Parse(customAppClusterJSONTpl))
|
|
|
|
|
|
|
|
func customAppClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
|
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := customAppClusterJSONTemplate.Execute(&buf, opts)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return buf.String()
|
|
|
|
}
|