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.
1171 lines
22 KiB
1171 lines
22 KiB
package write |
|
|
|
import ( |
|
"io" |
|
"os" |
|
"strings" |
|
"testing" |
|
"time" |
|
|
|
"github.com/hashicorp/consul/agent" |
|
"github.com/hashicorp/consul/api" |
|
"github.com/hashicorp/consul/sdk/testutil" |
|
"github.com/mitchellh/cli" |
|
"github.com/stretchr/testify/require" |
|
) |
|
|
|
func TestConfigWrite_noTabs(t *testing.T) { |
|
t.Parallel() |
|
|
|
require.NotContains(t, New(cli.NewMockUi()).Help(), "\t") |
|
} |
|
|
|
func TestConfigWrite(t *testing.T) { |
|
t.Parallel() |
|
|
|
a := agent.NewTestAgent(t, t.Name(), ``) |
|
defer a.Shutdown() |
|
client := a.Client() |
|
|
|
t.Run("File", func(t *testing.T) { |
|
ui := cli.NewMockUi() |
|
c := New(ui) |
|
|
|
f := testutil.TempFile(t, "config-write-svc-web.hcl") |
|
defer os.Remove(f.Name()) |
|
_, err := f.WriteString(` |
|
Kind = "service-defaults" |
|
Name = "web" |
|
Protocol = "udp" |
|
`) |
|
|
|
require.NoError(t, err) |
|
|
|
args := []string{ |
|
"-http-addr=" + a.HTTPAddr(), |
|
f.Name(), |
|
} |
|
|
|
code := c.Run(args) |
|
require.Empty(t, ui.ErrorWriter.String()) |
|
require.Equal(t, 0, code) |
|
|
|
entry, _, err := client.ConfigEntries().Get("service-defaults", "web", nil) |
|
require.NoError(t, err) |
|
svc, ok := entry.(*api.ServiceConfigEntry) |
|
require.True(t, ok) |
|
require.Equal(t, api.ServiceDefaults, svc.Kind) |
|
require.Equal(t, "web", svc.Name) |
|
require.Equal(t, "udp", svc.Protocol) |
|
}) |
|
|
|
t.Run("Stdin", func(t *testing.T) { |
|
stdinR, stdinW := io.Pipe() |
|
|
|
ui := cli.NewMockUi() |
|
c := New(ui) |
|
c.testStdin = stdinR |
|
|
|
go func() { |
|
stdinW.Write([]byte(`{ |
|
"Kind": "proxy-defaults", |
|
"Name": "global", |
|
"Config": { |
|
"foo": "bar", |
|
"bar": 1.0 |
|
} |
|
}`)) |
|
stdinW.Close() |
|
}() |
|
|
|
args := []string{ |
|
"-http-addr=" + a.HTTPAddr(), |
|
"-", |
|
} |
|
|
|
code := c.Run(args) |
|
require.Empty(t, ui.ErrorWriter.String()) |
|
require.Equal(t, 0, code) |
|
|
|
entry, _, err := client.ConfigEntries().Get(api.ProxyDefaults, api.ProxyConfigGlobal, nil) |
|
require.NoError(t, err) |
|
proxy, ok := entry.(*api.ProxyConfigEntry) |
|
require.True(t, ok) |
|
require.Equal(t, api.ProxyDefaults, proxy.Kind) |
|
require.Equal(t, api.ProxyConfigGlobal, proxy.Name) |
|
require.Equal(t, map[string]interface{}{"foo": "bar", "bar": 1.0}, proxy.Config) |
|
}) |
|
|
|
t.Run("No config", func(t *testing.T) { |
|
ui := cli.NewMockUi() |
|
c := New(ui) |
|
|
|
code := c.Run([]string{}) |
|
require.NotEqual(t, 0, code) |
|
require.NotEmpty(t, ui.ErrorWriter.String()) |
|
}) |
|
} |
|
|
|
// TestParseConfigEntry is the 'api' mirror image of |
|
// agent/structs/config_entry_test.go:TestDecodeConfigEntry |
|
func TestParseConfigEntry(t *testing.T) { |
|
t.Parallel() |
|
for _, tc := range []struct { |
|
name string |
|
camel, camelJSON string |
|
snake, snakeJSON string |
|
expect api.ConfigEntry |
|
expectJSON api.ConfigEntry |
|
expectErr string |
|
}{ |
|
{ |
|
name: "proxy-defaults: extra fields or typo", |
|
snake: ` |
|
kind = "proxy-defaults" |
|
name = "main" |
|
cornfig { |
|
"foo" = 19 |
|
} |
|
`, |
|
camel: ` |
|
Kind = "proxy-defaults" |
|
Name = "main" |
|
Cornfig { |
|
"foo" = 19 |
|
} |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "proxy-defaults", |
|
"name": "main", |
|
"cornfig": { |
|
"foo": 19 |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "proxy-defaults", |
|
"Name": "main", |
|
"Cornfig": { |
|
"foo": 19 |
|
} |
|
} |
|
`, |
|
expectErr: `invalid config key "cornfig"`, |
|
}, |
|
{ |
|
name: "proxy-defaults", |
|
snake: ` |
|
kind = "proxy-defaults" |
|
name = "main" |
|
config { |
|
"foo" = 19 |
|
"bar" = "abc" |
|
"moreconfig" { |
|
"moar" = "config" |
|
} |
|
} |
|
mesh_gateway { |
|
mode = "remote" |
|
} |
|
`, |
|
camel: ` |
|
Kind = "proxy-defaults" |
|
Name = "main" |
|
Config { |
|
"foo" = 19 |
|
"bar" = "abc" |
|
"moreconfig" { |
|
"moar" = "config" |
|
} |
|
} |
|
MeshGateway { |
|
Mode = "remote" |
|
} |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "proxy-defaults", |
|
"name": "main", |
|
"config": { |
|
"foo": 19, |
|
"bar": "abc", |
|
"moreconfig": { |
|
"moar": "config" |
|
} |
|
}, |
|
"mesh_gateway": { |
|
"mode": "remote" |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "proxy-defaults", |
|
"Name": "main", |
|
"Config": { |
|
"foo": 19, |
|
"bar": "abc", |
|
"moreconfig": { |
|
"moar": "config" |
|
} |
|
}, |
|
"MeshGateway": { |
|
"Mode": "remote" |
|
} |
|
} |
|
`, |
|
expect: &api.ProxyConfigEntry{ |
|
Kind: "proxy-defaults", |
|
Name: "main", |
|
Config: map[string]interface{}{ |
|
"foo": 19, |
|
"bar": "abc", |
|
"moreconfig": map[string]interface{}{ |
|
"moar": "config", |
|
}, |
|
}, |
|
MeshGateway: api.MeshGatewayConfig{ |
|
Mode: api.MeshGatewayModeRemote, |
|
}, |
|
}, |
|
expectJSON: &api.ProxyConfigEntry{ |
|
Kind: "proxy-defaults", |
|
Name: "main", |
|
Config: map[string]interface{}{ |
|
"foo": float64(19), // json decoding gives float64 instead of int here |
|
"bar": "abc", |
|
"moreconfig": map[string]interface{}{ |
|
"moar": "config", |
|
}, |
|
}, |
|
MeshGateway: api.MeshGatewayConfig{ |
|
Mode: api.MeshGatewayModeRemote, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-defaults", |
|
snake: ` |
|
kind = "service-defaults" |
|
name = "main" |
|
protocol = "http" |
|
external_sni = "abc-123" |
|
mesh_gateway { |
|
mode = "remote" |
|
} |
|
`, |
|
camel: ` |
|
Kind = "service-defaults" |
|
Name = "main" |
|
Protocol = "http" |
|
ExternalSNI = "abc-123" |
|
MeshGateway { |
|
Mode = "remote" |
|
} |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-defaults", |
|
"name": "main", |
|
"protocol": "http", |
|
"external_sni": "abc-123", |
|
"mesh_gateway": { |
|
"mode": "remote" |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-defaults", |
|
"Name": "main", |
|
"Protocol": "http", |
|
"ExternalSNI": "abc-123", |
|
"MeshGateway": { |
|
"Mode": "remote" |
|
} |
|
} |
|
`, |
|
expect: &api.ServiceConfigEntry{ |
|
Kind: "service-defaults", |
|
Name: "main", |
|
Protocol: "http", |
|
ExternalSNI: "abc-123", |
|
MeshGateway: api.MeshGatewayConfig{ |
|
Mode: api.MeshGatewayModeRemote, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-router: kitchen sink", |
|
snake: ` |
|
kind = "service-router" |
|
name = "main" |
|
routes = [ |
|
{ |
|
match { |
|
http { |
|
path_exact = "/foo" |
|
header = [ |
|
{ |
|
name = "debug1" |
|
present = true |
|
}, |
|
{ |
|
name = "debug2" |
|
present = false |
|
invert = true |
|
}, |
|
{ |
|
name = "debug3" |
|
exact = "1" |
|
}, |
|
{ |
|
name = "debug4" |
|
prefix = "aaa" |
|
}, |
|
{ |
|
name = "debug5" |
|
suffix = "bbb" |
|
}, |
|
{ |
|
name = "debug6" |
|
regex = "a.*z" |
|
}, |
|
] |
|
} |
|
} |
|
destination { |
|
service = "carrot" |
|
service_subset = "kale" |
|
namespace = "leek" |
|
prefix_rewrite = "/alternate" |
|
request_timeout = "99s" |
|
num_retries = 12345 |
|
retry_on_connect_failure = true |
|
retry_on_status_codes = [401, 209] |
|
} |
|
}, |
|
{ |
|
match { |
|
http { |
|
path_prefix = "/foo" |
|
methods = [ "GET", "DELETE" ] |
|
query_param = [ |
|
{ |
|
name = "hack1" |
|
present = true |
|
}, |
|
{ |
|
name = "hack2" |
|
exact = "1" |
|
}, |
|
{ |
|
name = "hack3" |
|
regex = "a.*z" |
|
}, |
|
] |
|
} |
|
} |
|
}, |
|
{ |
|
match { |
|
http { |
|
path_regex = "/foo" |
|
} |
|
} |
|
}, |
|
] |
|
`, |
|
camel: ` |
|
Kind = "service-router" |
|
Name = "main" |
|
Routes = [ |
|
{ |
|
Match { |
|
HTTP { |
|
PathExact = "/foo" |
|
Header = [ |
|
{ |
|
Name = "debug1" |
|
Present = true |
|
}, |
|
{ |
|
Name = "debug2" |
|
Present = false |
|
Invert = true |
|
}, |
|
{ |
|
Name = "debug3" |
|
Exact = "1" |
|
}, |
|
{ |
|
Name = "debug4" |
|
Prefix = "aaa" |
|
}, |
|
{ |
|
Name = "debug5" |
|
Suffix = "bbb" |
|
}, |
|
{ |
|
Name = "debug6" |
|
Regex = "a.*z" |
|
}, |
|
] |
|
} |
|
} |
|
Destination { |
|
Service = "carrot" |
|
ServiceSubset = "kale" |
|
Namespace = "leek" |
|
PrefixRewrite = "/alternate" |
|
RequestTimeout = "99s" |
|
NumRetries = 12345 |
|
RetryOnConnectFailure = true |
|
RetryOnStatusCodes = [401, 209] |
|
} |
|
}, |
|
{ |
|
Match { |
|
HTTP { |
|
PathPrefix = "/foo" |
|
Methods = [ "GET", "DELETE" ] |
|
QueryParam = [ |
|
{ |
|
Name = "hack1" |
|
Present = true |
|
}, |
|
{ |
|
Name = "hack2" |
|
Exact = "1" |
|
}, |
|
{ |
|
Name = "hack3" |
|
Regex = "a.*z" |
|
}, |
|
] |
|
} |
|
} |
|
}, |
|
{ |
|
Match { |
|
HTTP { |
|
PathRegex = "/foo" |
|
} |
|
} |
|
}, |
|
] |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-router", |
|
"name": "main", |
|
"routes": [ |
|
{ |
|
"match": { |
|
"http": { |
|
"path_exact": "/foo", |
|
"header": [ |
|
{ |
|
"name": "debug1", |
|
"present": true |
|
}, |
|
{ |
|
"name": "debug2", |
|
"present": false, |
|
"invert": true |
|
}, |
|
{ |
|
"name": "debug3", |
|
"exact": "1" |
|
}, |
|
{ |
|
"name": "debug4", |
|
"prefix": "aaa" |
|
}, |
|
{ |
|
"name": "debug5", |
|
"suffix": "bbb" |
|
}, |
|
{ |
|
"name": "debug6", |
|
"regex": "a.*z" |
|
} |
|
] |
|
} |
|
}, |
|
"destination": { |
|
"service": "carrot", |
|
"service_subset": "kale", |
|
"namespace": "leek", |
|
"prefix_rewrite": "/alternate", |
|
"request_timeout": "99s", |
|
"num_retries": 12345, |
|
"retry_on_connect_failure": true, |
|
"retry_on_status_codes": [ |
|
401, |
|
209 |
|
] |
|
} |
|
}, |
|
{ |
|
"match": { |
|
"http": { |
|
"path_prefix": "/foo", |
|
"methods": [ |
|
"GET", |
|
"DELETE" |
|
], |
|
"query_param": [ |
|
{ |
|
"name": "hack1", |
|
"present": true |
|
}, |
|
{ |
|
"name": "hack2", |
|
"exact": "1" |
|
}, |
|
{ |
|
"name": "hack3", |
|
"regex": "a.*z" |
|
} |
|
] |
|
} |
|
} |
|
}, |
|
{ |
|
"match": { |
|
"http": { |
|
"path_regex": "/foo" |
|
} |
|
} |
|
} |
|
] |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-router", |
|
"Name": "main", |
|
"Routes": [ |
|
{ |
|
"Match": { |
|
"HTTP": { |
|
"PathExact": "/foo", |
|
"Header": [ |
|
{ |
|
"Name": "debug1", |
|
"Present": true |
|
}, |
|
{ |
|
"Name": "debug2", |
|
"Present": false, |
|
"Invert": true |
|
}, |
|
{ |
|
"Name": "debug3", |
|
"Exact": "1" |
|
}, |
|
{ |
|
"Name": "debug4", |
|
"Prefix": "aaa" |
|
}, |
|
{ |
|
"Name": "debug5", |
|
"Suffix": "bbb" |
|
}, |
|
{ |
|
"Name": "debug6", |
|
"Regex": "a.*z" |
|
} |
|
] |
|
} |
|
}, |
|
"Destination": { |
|
"Service": "carrot", |
|
"ServiceSubset": "kale", |
|
"Namespace": "leek", |
|
"PrefixRewrite": "/alternate", |
|
"RequestTimeout": "99s", |
|
"NumRetries": 12345, |
|
"RetryOnConnectFailure": true, |
|
"RetryOnStatusCodes": [ |
|
401, |
|
209 |
|
] |
|
} |
|
}, |
|
{ |
|
"Match": { |
|
"HTTP": { |
|
"PathPrefix": "/foo", |
|
"Methods": [ |
|
"GET", |
|
"DELETE" |
|
], |
|
"QueryParam": [ |
|
{ |
|
"Name": "hack1", |
|
"Present": true |
|
}, |
|
{ |
|
"Name": "hack2", |
|
"Exact": "1" |
|
}, |
|
{ |
|
"Name": "hack3", |
|
"Regex": "a.*z" |
|
} |
|
] |
|
} |
|
} |
|
}, |
|
{ |
|
"Match": { |
|
"HTTP": { |
|
"PathRegex": "/foo" |
|
} |
|
} |
|
} |
|
] |
|
} |
|
`, |
|
expect: &api.ServiceRouterConfigEntry{ |
|
Kind: "service-router", |
|
Name: "main", |
|
Routes: []api.ServiceRoute{ |
|
{ |
|
Match: &api.ServiceRouteMatch{ |
|
HTTP: &api.ServiceRouteHTTPMatch{ |
|
PathExact: "/foo", |
|
Header: []api.ServiceRouteHTTPMatchHeader{ |
|
{ |
|
Name: "debug1", |
|
Present: true, |
|
}, |
|
{ |
|
Name: "debug2", |
|
Present: false, |
|
Invert: true, |
|
}, |
|
{ |
|
Name: "debug3", |
|
Exact: "1", |
|
}, |
|
{ |
|
Name: "debug4", |
|
Prefix: "aaa", |
|
}, |
|
{ |
|
Name: "debug5", |
|
Suffix: "bbb", |
|
}, |
|
{ |
|
Name: "debug6", |
|
Regex: "a.*z", |
|
}, |
|
}, |
|
}, |
|
}, |
|
Destination: &api.ServiceRouteDestination{ |
|
Service: "carrot", |
|
ServiceSubset: "kale", |
|
Namespace: "leek", |
|
PrefixRewrite: "/alternate", |
|
RequestTimeout: 99 * time.Second, |
|
NumRetries: 12345, |
|
RetryOnConnectFailure: true, |
|
RetryOnStatusCodes: []uint32{401, 209}, |
|
}, |
|
}, |
|
{ |
|
Match: &api.ServiceRouteMatch{ |
|
HTTP: &api.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/foo", |
|
Methods: []string{"GET", "DELETE"}, |
|
QueryParam: []api.ServiceRouteHTTPMatchQueryParam{ |
|
{ |
|
Name: "hack1", |
|
Present: true, |
|
}, |
|
{ |
|
Name: "hack2", |
|
Exact: "1", |
|
}, |
|
{ |
|
Name: "hack3", |
|
Regex: "a.*z", |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
Match: &api.ServiceRouteMatch{ |
|
HTTP: &api.ServiceRouteHTTPMatch{ |
|
PathRegex: "/foo", |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-splitter: kitchen sink", |
|
snake: ` |
|
kind = "service-splitter" |
|
name = "main" |
|
splits = [ |
|
{ |
|
weight = 97.1 |
|
service_subset = "v1" |
|
}, |
|
{ |
|
weight = 2 |
|
service_subset = "v2" |
|
}, |
|
{ |
|
weight = 0.9 |
|
service = "other" |
|
namespace = "alt" |
|
}, |
|
] |
|
`, |
|
camel: ` |
|
Kind = "service-splitter" |
|
Name = "main" |
|
Splits = [ |
|
{ |
|
Weight = 97.1 |
|
ServiceSubset = "v1" |
|
}, |
|
{ |
|
Weight = 2, |
|
ServiceSubset = "v2" |
|
}, |
|
{ |
|
Weight = 0.9 |
|
Service = "other" |
|
Namespace = "alt" |
|
}, |
|
] |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-splitter", |
|
"name": "main", |
|
"splits": [ |
|
{ |
|
"weight": 97.1, |
|
"service_subset": "v1" |
|
}, |
|
{ |
|
"weight": 2, |
|
"service_subset": "v2" |
|
}, |
|
{ |
|
"weight": 0.9, |
|
"service": "other", |
|
"namespace": "alt" |
|
} |
|
] |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-splitter", |
|
"Name": "main", |
|
"Splits": [ |
|
{ |
|
"Weight": 97.1, |
|
"ServiceSubset": "v1" |
|
}, |
|
{ |
|
"Weight": 2, |
|
"ServiceSubset": "v2" |
|
}, |
|
{ |
|
"Weight": 0.9, |
|
"Service": "other", |
|
"Namespace": "alt" |
|
} |
|
] |
|
} |
|
`, |
|
expect: &api.ServiceSplitterConfigEntry{ |
|
Kind: api.ServiceSplitter, |
|
Name: "main", |
|
Splits: []api.ServiceSplit{ |
|
{ |
|
Weight: 97.1, |
|
ServiceSubset: "v1", |
|
}, |
|
{ |
|
Weight: 2, |
|
ServiceSubset: "v2", |
|
}, |
|
{ |
|
Weight: 0.9, |
|
Service: "other", |
|
Namespace: "alt", |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-resolver: subsets with failover", |
|
snake: ` |
|
kind = "service-resolver" |
|
name = "main" |
|
default_subset = "v1" |
|
connect_timeout = "15s" |
|
subsets = { |
|
"v1" = { |
|
filter = "Service.Meta.version == v1" |
|
}, |
|
"v2" = { |
|
filter = "Service.Meta.version == v2" |
|
only_passing = true |
|
}, |
|
} |
|
failover = { |
|
"v2" = { |
|
service = "failcopy" |
|
service_subset = "sure" |
|
namespace = "neighbor" |
|
datacenters = ["dc5", "dc14"] |
|
}, |
|
"*" = { |
|
datacenters = ["dc7"] |
|
} |
|
}`, |
|
camel: ` |
|
Kind = "service-resolver" |
|
Name = "main" |
|
DefaultSubset = "v1" |
|
ConnectTimeout = "15s" |
|
Subsets = { |
|
"v1" = { |
|
Filter = "Service.Meta.version == v1" |
|
}, |
|
"v2" = { |
|
Filter = "Service.Meta.version == v2" |
|
OnlyPassing = true |
|
}, |
|
} |
|
Failover = { |
|
"v2" = { |
|
Service = "failcopy" |
|
ServiceSubset = "sure" |
|
Namespace = "neighbor" |
|
Datacenters = ["dc5", "dc14"] |
|
}, |
|
"*" = { |
|
Datacenters = ["dc7"] |
|
} |
|
}`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-resolver", |
|
"name": "main", |
|
"default_subset": "v1", |
|
"connect_timeout": "15s", |
|
"subsets": { |
|
"v1": { |
|
"filter": "Service.Meta.version == v1" |
|
}, |
|
"v2": { |
|
"filter": "Service.Meta.version == v2", |
|
"only_passing": true |
|
} |
|
}, |
|
"failover": { |
|
"v2": { |
|
"service": "failcopy", |
|
"service_subset": "sure", |
|
"namespace": "neighbor", |
|
"datacenters": [ |
|
"dc5", |
|
"dc14" |
|
] |
|
}, |
|
"*": { |
|
"datacenters": [ |
|
"dc7" |
|
] |
|
} |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-resolver", |
|
"Name": "main", |
|
"DefaultSubset": "v1", |
|
"ConnectTimeout": "15s", |
|
"Subsets": { |
|
"v1": { |
|
"Filter": "Service.Meta.version == v1" |
|
}, |
|
"v2": { |
|
"Filter": "Service.Meta.version == v2", |
|
"OnlyPassing": true |
|
} |
|
}, |
|
"Failover": { |
|
"v2": { |
|
"Service": "failcopy", |
|
"ServiceSubset": "sure", |
|
"Namespace": "neighbor", |
|
"Datacenters": [ |
|
"dc5", |
|
"dc14" |
|
] |
|
}, |
|
"*": { |
|
"Datacenters": [ |
|
"dc7" |
|
] |
|
} |
|
} |
|
} |
|
`, |
|
expect: &api.ServiceResolverConfigEntry{ |
|
Kind: "service-resolver", |
|
Name: "main", |
|
DefaultSubset: "v1", |
|
ConnectTimeout: 15 * time.Second, |
|
Subsets: map[string]api.ServiceResolverSubset{ |
|
"v1": { |
|
Filter: "Service.Meta.version == v1", |
|
}, |
|
"v2": { |
|
Filter: "Service.Meta.version == v2", |
|
OnlyPassing: true, |
|
}, |
|
}, |
|
Failover: map[string]api.ServiceResolverFailover{ |
|
"v2": { |
|
Service: "failcopy", |
|
ServiceSubset: "sure", |
|
Namespace: "neighbor", |
|
Datacenters: []string{"dc5", "dc14"}, |
|
}, |
|
"*": { |
|
Datacenters: []string{"dc7"}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-resolver: redirect", |
|
snake: ` |
|
kind = "service-resolver" |
|
name = "main" |
|
redirect { |
|
service = "other" |
|
service_subset = "backup" |
|
namespace = "alt" |
|
datacenter = "dc9" |
|
} |
|
`, |
|
camel: ` |
|
Kind = "service-resolver" |
|
Name = "main" |
|
Redirect { |
|
Service = "other" |
|
ServiceSubset = "backup" |
|
Namespace = "alt" |
|
Datacenter = "dc9" |
|
} |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-resolver", |
|
"name": "main", |
|
"redirect": { |
|
"service": "other", |
|
"service_subset": "backup", |
|
"namespace": "alt", |
|
"datacenter": "dc9" |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-resolver", |
|
"Name": "main", |
|
"Redirect": { |
|
"Service": "other", |
|
"ServiceSubset": "backup", |
|
"Namespace": "alt", |
|
"Datacenter": "dc9" |
|
} |
|
} |
|
`, |
|
expect: &api.ServiceResolverConfigEntry{ |
|
Kind: "service-resolver", |
|
Name: "main", |
|
Redirect: &api.ServiceResolverRedirect{ |
|
Service: "other", |
|
ServiceSubset: "backup", |
|
Namespace: "alt", |
|
Datacenter: "dc9", |
|
}, |
|
}, |
|
}, |
|
{ |
|
name: "service-resolver: default", |
|
snake: ` |
|
kind = "service-resolver" |
|
name = "main" |
|
`, |
|
camel: ` |
|
Kind = "service-resolver" |
|
Name = "main" |
|
`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "service-resolver", |
|
"name": "main" |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "service-resolver", |
|
"Name": "main" |
|
} |
|
`, |
|
expect: &api.ServiceResolverConfigEntry{ |
|
Kind: "service-resolver", |
|
Name: "main", |
|
}, |
|
}, |
|
{ |
|
name: "expose paths: kitchen sink proxy defaults", |
|
snake: ` |
|
kind = "proxy-defaults" |
|
name = "global" |
|
expose = { |
|
checks = true |
|
paths = [ |
|
{ |
|
local_path_port = 8080 |
|
listener_port = 21500 |
|
path = "/healthz" |
|
protocol = "http2" |
|
} |
|
] |
|
}`, |
|
camel: ` |
|
Kind = "proxy-defaults" |
|
Name = "global" |
|
Expose = { |
|
Checks = true |
|
Paths = [ |
|
{ |
|
LocalPathPort = 8080 |
|
ListenerPort = 21500 |
|
Path = "/healthz" |
|
Protocol = "http2" |
|
} |
|
] |
|
}`, |
|
snakeJSON: ` |
|
{ |
|
"kind": "proxy-defaults", |
|
"name": "global", |
|
"expose": { |
|
"checks": true, |
|
"paths": [ |
|
{ |
|
"local_path_port": 8080, |
|
"listener_port": 21500, |
|
"path": "/healthz", |
|
"protocol": "http2" |
|
} |
|
] |
|
} |
|
} |
|
`, |
|
camelJSON: ` |
|
{ |
|
"Kind": "proxy-defaults", |
|
"Name": "global", |
|
"Expose": { |
|
"Checks": true, |
|
"Paths": [ |
|
{ |
|
"LocalPathPort": 8080, |
|
"ListenerPort": 21500, |
|
"Path": "/healthz", |
|
"Protocol": "http2" |
|
} |
|
] |
|
} |
|
} |
|
`, |
|
expect: &api.ProxyConfigEntry{ |
|
Kind: "proxy-defaults", |
|
Name: "global", |
|
Expose: api.ExposeConfig{ |
|
Checks: true, |
|
Paths: []api.ExposePath{ |
|
{ |
|
ListenerPort: 21500, |
|
Path: "/healthz", |
|
LocalPathPort: 8080, |
|
Protocol: "http2", |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
} { |
|
tc := tc |
|
|
|
testbody := func(t *testing.T, body string, expect api.ConfigEntry) { |
|
t.Helper() |
|
got, err := parseConfigEntry(body) |
|
if tc.expectErr != "" { |
|
require.Nil(t, got) |
|
require.Error(t, err) |
|
requireContainsLower(t, err.Error(), tc.expectErr) |
|
} else { |
|
require.NoError(t, err) |
|
require.Equal(t, expect, got) |
|
} |
|
} |
|
|
|
t.Run(tc.name+" (hcl snake case)", func(t *testing.T) { |
|
testbody(t, tc.snake, tc.expect) |
|
}) |
|
t.Run(tc.name+" (hcl camel case)", func(t *testing.T) { |
|
testbody(t, tc.camel, tc.expect) |
|
}) |
|
if tc.snakeJSON != "" { |
|
t.Run(tc.name+" (json snake case)", func(t *testing.T) { |
|
if tc.expectJSON != nil { |
|
testbody(t, tc.snakeJSON, tc.expectJSON) |
|
} else { |
|
testbody(t, tc.snakeJSON, tc.expect) |
|
} |
|
}) |
|
} |
|
if tc.camelJSON != "" { |
|
t.Run(tc.name+" (json camel case)", func(t *testing.T) { |
|
if tc.expectJSON != nil { |
|
testbody(t, tc.camelJSON, tc.expectJSON) |
|
} else { |
|
testbody(t, tc.camelJSON, tc.expect) |
|
} |
|
}) |
|
} |
|
} |
|
} |
|
|
|
func requireContainsLower(t *testing.T, haystack, needle string) { |
|
t.Helper() |
|
require.Contains(t, strings.ToLower(haystack), strings.ToLower(needle)) |
|
}
|
|
|