Merge pull request #11366 from hashicorp/service-exports
oss: Add new service-exports config entry
This commit is contained in:
commit
45192f278b
|
@ -360,6 +360,7 @@ func validateProposedConfigEntryInGraph(
|
||||||
}
|
}
|
||||||
case structs.ServiceIntentions:
|
case structs.ServiceIntentions:
|
||||||
case structs.MeshConfig:
|
case structs.MeshConfig:
|
||||||
|
case structs.ServiceExports:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unhandled kind %q during validation of %q", kindName.Kind, kindName.Name)
|
return fmt.Errorf("unhandled kind %q during validation of %q", kindName.Kind, kindName.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,14 @@ func TestUsageReporter_emitNodeUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
||||||
},
|
},
|
||||||
|
@ -354,6 +362,14 @@ func TestUsageReporter_emitNodeUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -559,6 +575,14 @@ func TestUsageReporter_emitServiceUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
||||||
},
|
},
|
||||||
|
@ -778,6 +802,14 @@ func TestUsageReporter_emitServiceUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -974,6 +1006,14 @@ func TestUsageReporter_emitKVUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
||||||
},
|
},
|
||||||
|
@ -1160,6 +1200,14 @@ func TestUsageReporter_emitKVUsage_OSS(t *testing.T) {
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=service-exports": {
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-exports"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ const (
|
||||||
TerminatingGateway string = "terminating-gateway"
|
TerminatingGateway string = "terminating-gateway"
|
||||||
ServiceIntentions string = "service-intentions"
|
ServiceIntentions string = "service-intentions"
|
||||||
MeshConfig string = "mesh"
|
MeshConfig string = "mesh"
|
||||||
|
ServiceExports string = "service-exports"
|
||||||
|
|
||||||
ProxyConfigGlobal string = "global"
|
ProxyConfigGlobal string = "global"
|
||||||
MeshConfigMesh string = "mesh"
|
MeshConfigMesh string = "mesh"
|
||||||
|
@ -44,6 +45,7 @@ var AllConfigEntryKinds = []string{
|
||||||
TerminatingGateway,
|
TerminatingGateway,
|
||||||
ServiceIntentions,
|
ServiceIntentions,
|
||||||
MeshConfig,
|
MeshConfig,
|
||||||
|
ServiceExports,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEntry is the interface for centralized configuration stored in Raft.
|
// ConfigEntry is the interface for centralized configuration stored in Raft.
|
||||||
|
@ -530,6 +532,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &ServiceIntentionsConfigEntry{Name: name}, nil
|
return &ServiceIntentionsConfigEntry{Name: name}, nil
|
||||||
case MeshConfig:
|
case MeshConfig:
|
||||||
return &MeshConfigEntry{}, nil
|
return &MeshConfigEntry{}, nil
|
||||||
|
case ServiceExports:
|
||||||
|
return &ServiceExportsConfigEntry{Partition: name}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||||
}
|
}
|
||||||
|
|
145
agent/structs/config_entry_exports.go
Normal file
145
agent/structs/config_entry_exports.go
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceExportsConfigEntry is the top-level struct for exporting a service to be exposed
|
||||||
|
// across other admin partitions.
|
||||||
|
type ServiceExportsConfigEntry struct {
|
||||||
|
Partition string
|
||||||
|
|
||||||
|
// Services is a list of services to be exported and the list of partitions
|
||||||
|
// to expose them to.
|
||||||
|
Services []ExportedService
|
||||||
|
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
|
RaftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportedService manages the exporting of a service in the local partition to
|
||||||
|
// other partitions.
|
||||||
|
type ExportedService struct {
|
||||||
|
// Name is the name of the service to be exported.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Namespace is the namespace to export the service from.
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Consumers is a list of downstream consumers of the service to be exported.
|
||||||
|
Consumers []ServiceConsumer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceConsumer represents a downstream consumer of the service to be exported.
|
||||||
|
type ServiceConsumer struct {
|
||||||
|
// Partition is the admin partition to export the service to.
|
||||||
|
Partition string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) Clone() *ServiceExportsConfigEntry {
|
||||||
|
e2 := *e
|
||||||
|
e2.Services = make([]ExportedService, len(e.Services))
|
||||||
|
for _, svc := range e.Services {
|
||||||
|
exportedSvc := svc
|
||||||
|
exportedSvc.Consumers = make([]ServiceConsumer, len(svc.Consumers))
|
||||||
|
for _, consumer := range svc.Consumers {
|
||||||
|
exportedSvc.Consumers = append(exportedSvc.Consumers, consumer)
|
||||||
|
}
|
||||||
|
e2.Services = append(e2.Services, exportedSvc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetKind() string {
|
||||||
|
return ServiceExports
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetName() string {
|
||||||
|
if e == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Partition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetMeta() map[string]string {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) Normalize() error {
|
||||||
|
if e == nil {
|
||||||
|
return fmt.Errorf("config entry is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := DefaultEnterpriseMetaInPartition(e.Partition)
|
||||||
|
e.EnterpriseMeta.Merge(meta)
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
|
for i := range e.Services {
|
||||||
|
e.Services[i].Namespace = NamespaceOrDefault(e.Services[i].Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) Validate() error {
|
||||||
|
if e.Partition == "" {
|
||||||
|
return fmt.Errorf("Partition is required")
|
||||||
|
}
|
||||||
|
if e.Partition == WildcardSpecifier {
|
||||||
|
return fmt.Errorf("service-exports Partition must be the name of a partition, and not a wildcard")
|
||||||
|
}
|
||||||
|
|
||||||
|
validationErr := validateConfigEntryMeta(e.Meta)
|
||||||
|
|
||||||
|
for _, svc := range e.Services {
|
||||||
|
if svc.Name == "" {
|
||||||
|
return fmt.Errorf("service name cannot be empty")
|
||||||
|
}
|
||||||
|
if len(svc.Consumers) == 0 {
|
||||||
|
return fmt.Errorf("service %q must have at least one consumer", svc.Name)
|
||||||
|
}
|
||||||
|
for _, consumer := range svc.Consumers {
|
||||||
|
if consumer.Partition == WildcardSpecifier {
|
||||||
|
return fmt.Errorf("exporting to all partitions (wildcard) is not yet supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validationErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) CanRead(authz acl.Authorizer) bool {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.MeshRead(&authzContext) == acl.Allow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) CanWrite(authz acl.Authorizer) bool {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.MeshWrite(&authzContext) == acl.Allow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
if e == nil {
|
||||||
|
return &RaftIndex{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.RaftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
|
@ -1664,6 +1664,102 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "service-exports",
|
||||||
|
snake: `
|
||||||
|
kind = "service-exports"
|
||||||
|
partition = "foo"
|
||||||
|
meta {
|
||||||
|
"foo" = "bar"
|
||||||
|
"gir" = "zim"
|
||||||
|
}
|
||||||
|
services = [
|
||||||
|
{
|
||||||
|
name = "web"
|
||||||
|
namespace = "foo"
|
||||||
|
consumers = [
|
||||||
|
{
|
||||||
|
partition = "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
partition = "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "db"
|
||||||
|
namespace = "bar"
|
||||||
|
consumers = [
|
||||||
|
{
|
||||||
|
partition = "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`,
|
||||||
|
camel: `
|
||||||
|
Kind = "service-exports"
|
||||||
|
Partition = "foo"
|
||||||
|
Meta {
|
||||||
|
"foo" = "bar"
|
||||||
|
"gir" = "zim"
|
||||||
|
}
|
||||||
|
Services = [
|
||||||
|
{
|
||||||
|
Name = "web"
|
||||||
|
Namespace = "foo"
|
||||||
|
Consumers = [
|
||||||
|
{
|
||||||
|
Partition = "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Partition = "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name = "db"
|
||||||
|
Namespace = "bar"
|
||||||
|
Consumers = [
|
||||||
|
{
|
||||||
|
Partition = "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`,
|
||||||
|
expect: &ServiceExportsConfigEntry{
|
||||||
|
Partition: "foo",
|
||||||
|
Meta: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"gir": "zim",
|
||||||
|
},
|
||||||
|
Services: []ExportedService{
|
||||||
|
{
|
||||||
|
Name: "web",
|
||||||
|
Namespace: "foo",
|
||||||
|
Consumers: []ServiceConsumer{
|
||||||
|
{
|
||||||
|
Partition: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Partition: "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "db",
|
||||||
|
Namespace: "bar",
|
||||||
|
Consumers: []ServiceConsumer{
|
||||||
|
{
|
||||||
|
Partition: "zoo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EnterpriseMeta: NewEnterpriseMetaWithPartition("foo", ""),
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
tc := tc
|
tc := tc
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ const (
|
||||||
TerminatingGateway string = "terminating-gateway"
|
TerminatingGateway string = "terminating-gateway"
|
||||||
ServiceIntentions string = "service-intentions"
|
ServiceIntentions string = "service-intentions"
|
||||||
MeshConfig string = "mesh"
|
MeshConfig string = "mesh"
|
||||||
|
ServiceExports string = "service-exports"
|
||||||
|
|
||||||
ProxyConfigGlobal string = "global"
|
ProxyConfigGlobal string = "global"
|
||||||
MeshConfigMesh string = "mesh"
|
MeshConfigMesh string = "mesh"
|
||||||
|
@ -276,6 +277,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &ServiceIntentionsConfigEntry{Kind: kind, Name: name}, nil
|
return &ServiceIntentionsConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case MeshConfig:
|
case MeshConfig:
|
||||||
return &MeshConfigEntry{}, nil
|
return &MeshConfigEntry{}, nil
|
||||||
|
case ServiceExports:
|
||||||
|
return &ServiceExportsConfigEntry{Partition: name}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||||
}
|
}
|
||||||
|
|
67
api/config_entry_exports.go
Normal file
67
api/config_entry_exports.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// ServiceExportsConfigEntry manages the exported services for a single admin partition.
|
||||||
|
// Admin Partitions are a Consul Enterprise feature.
|
||||||
|
type ServiceExportsConfigEntry struct {
|
||||||
|
// Partition is the partition the ServiceExportsConfigEntry applies to.
|
||||||
|
// Partitioning is a Consul Enterprise feature.
|
||||||
|
Partition string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Services is a list of services to be exported and the list of partitions
|
||||||
|
// to expose them to.
|
||||||
|
Services []ExportedService
|
||||||
|
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
|
||||||
|
// CreateIndex is the Raft index this entry was created at. This is a
|
||||||
|
// read-only field.
|
||||||
|
CreateIndex uint64
|
||||||
|
|
||||||
|
// ModifyIndex is used for the Check-And-Set operations and can also be fed
|
||||||
|
// back into the WaitIndex of the QueryOptions in order to perform blocking
|
||||||
|
// queries.
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportedService manages the exporting of a service in the local partition to
|
||||||
|
// other partitions.
|
||||||
|
type ExportedService struct {
|
||||||
|
// Name is the name of the service to be exported.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Namespace is the namespace to export the service from.
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Consumers is a list of downstream consumers of the service to be exported.
|
||||||
|
Consumers []ServiceConsumer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceConsumer represents a downstream consumer of the service to be exported.
|
||||||
|
type ServiceConsumer struct {
|
||||||
|
// Partition is the admin partition to export the service to.
|
||||||
|
Partition string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceExportsConfigEntry) GetKind() string { return ServiceExports }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetName() string { return e.Partition }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetPartition() string { return e.Partition }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetNamespace() string { return IntentionDefaultNamespace }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetCreateIndex() uint64 { return e.CreateIndex }
|
||||||
|
func (e *ServiceExportsConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex }
|
||||||
|
|
||||||
|
// MarshalJSON adds the Kind field so that the JSON can be decoded back into the
|
||||||
|
// correct type.
|
||||||
|
func (e *ServiceExportsConfigEntry) MarshalJSON() ([]byte, error) {
|
||||||
|
type Alias ServiceExportsConfigEntry
|
||||||
|
source := &struct {
|
||||||
|
Kind string
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Kind: ServiceExports,
|
||||||
|
Alias: (*Alias)(e),
|
||||||
|
}
|
||||||
|
return json.Marshal(source)
|
||||||
|
}
|
|
@ -2721,6 +2721,167 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "service-exports",
|
||||||
|
snake: `
|
||||||
|
kind = "service-exports"
|
||||||
|
partition = "foo"
|
||||||
|
meta {
|
||||||
|
"foo" = "bar"
|
||||||
|
"gir" = "zim"
|
||||||
|
}
|
||||||
|
services = [
|
||||||
|
{
|
||||||
|
name = "web"
|
||||||
|
namespace = "foo"
|
||||||
|
consumers = [
|
||||||
|
{
|
||||||
|
partition = "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
partition = "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "db"
|
||||||
|
namespace = "bar"
|
||||||
|
consumers = [
|
||||||
|
{
|
||||||
|
partition = "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`,
|
||||||
|
camel: `
|
||||||
|
Kind = "service-exports"
|
||||||
|
Partition = "foo"
|
||||||
|
Meta {
|
||||||
|
"foo" = "bar"
|
||||||
|
"gir" = "zim"
|
||||||
|
}
|
||||||
|
Services = [
|
||||||
|
{
|
||||||
|
Name = "web"
|
||||||
|
Namespace = "foo"
|
||||||
|
Consumers = [
|
||||||
|
{
|
||||||
|
Partition = "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Partition = "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name = "db"
|
||||||
|
Namespace = "bar"
|
||||||
|
Consumers = [
|
||||||
|
{
|
||||||
|
Partition = "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`,
|
||||||
|
snakeJSON: `
|
||||||
|
{
|
||||||
|
"kind": "service-exports",
|
||||||
|
"partition": "foo",
|
||||||
|
"meta": {
|
||||||
|
"foo": "bar",
|
||||||
|
"gir": "zim"
|
||||||
|
},
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"namespace": "foo",
|
||||||
|
"consumers": [
|
||||||
|
{
|
||||||
|
"partition": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"partition": "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "db",
|
||||||
|
"namespace": "bar",
|
||||||
|
"consumers": [
|
||||||
|
{
|
||||||
|
"partition": "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
camelJSON: `
|
||||||
|
{
|
||||||
|
"Kind": "service-exports",
|
||||||
|
"Partition": "foo",
|
||||||
|
"Meta": {
|
||||||
|
"foo": "bar",
|
||||||
|
"gir": "zim"
|
||||||
|
},
|
||||||
|
"Services": [
|
||||||
|
{
|
||||||
|
"Name": "web",
|
||||||
|
"Namespace": "foo",
|
||||||
|
"Consumers": [
|
||||||
|
{
|
||||||
|
"Partition": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Partition": "baz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "db",
|
||||||
|
"Namespace": "bar",
|
||||||
|
"Consumers": [
|
||||||
|
{
|
||||||
|
"Partition": "zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
expect: &api.ServiceExportsConfigEntry{
|
||||||
|
Partition: "foo",
|
||||||
|
Meta: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"gir": "zim",
|
||||||
|
},
|
||||||
|
Services: []api.ExportedService{
|
||||||
|
{
|
||||||
|
Name: "web",
|
||||||
|
Namespace: "foo",
|
||||||
|
Consumers: []api.ServiceConsumer{
|
||||||
|
{
|
||||||
|
Partition: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Partition: "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "db",
|
||||||
|
Namespace: "bar",
|
||||||
|
Consumers: []api.ServiceConsumer{
|
||||||
|
{
|
||||||
|
Partition: "zoo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
tc := tc
|
tc := tc
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue