2019-03-19 17:06:46 +00:00
|
|
|
package structs
|
|
|
|
|
2019-03-27 23:52:38 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2019-04-18 04:35:19 +00:00
|
|
|
"strconv"
|
2019-03-27 23:52:38 +00:00
|
|
|
"strings"
|
2019-03-28 06:56:35 +00:00
|
|
|
|
2019-04-10 21:27:28 +00:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2019-04-18 04:35:19 +00:00
|
|
|
"github.com/hashicorp/consul/agent/cache"
|
2019-03-28 06:56:35 +00:00
|
|
|
"github.com/hashicorp/go-msgpack/codec"
|
2019-04-18 04:35:19 +00:00
|
|
|
"github.com/mitchellh/hashstructure"
|
2019-03-27 23:52:38 +00:00
|
|
|
)
|
2019-03-22 16:25:37 +00:00
|
|
|
|
2019-03-19 17:06:46 +00:00
|
|
|
const (
|
2019-03-19 22:56:17 +00:00
|
|
|
ServiceDefaults string = "service-defaults"
|
|
|
|
ProxyDefaults string = "proxy-defaults"
|
2019-03-22 16:25:37 +00:00
|
|
|
|
|
|
|
ProxyConfigGlobal string = "global"
|
2019-03-27 23:52:38 +00:00
|
|
|
|
|
|
|
DefaultServiceProtocol = "tcp"
|
2019-03-19 17:06:46 +00:00
|
|
|
)
|
|
|
|
|
2019-03-19 22:56:17 +00:00
|
|
|
// ConfigEntry is the
|
|
|
|
type ConfigEntry interface {
|
|
|
|
GetKind() string
|
2019-03-19 17:06:46 +00:00
|
|
|
GetName() string
|
2019-03-19 22:56:17 +00:00
|
|
|
|
2019-03-27 23:52:38 +00:00
|
|
|
// This is called in the RPC endpoint and can apply defaults or limits.
|
2019-03-19 22:56:17 +00:00
|
|
|
Normalize() error
|
2019-03-19 17:06:46 +00:00
|
|
|
Validate() error
|
2019-03-19 22:56:17 +00:00
|
|
|
|
2019-04-23 06:55:11 +00:00
|
|
|
// CanRead and CanWrite return whether or not the given Authorizer
|
2019-04-10 21:27:28 +00:00
|
|
|
// has permission to read or write to the config entry, respectively.
|
2019-04-23 06:55:11 +00:00
|
|
|
CanRead(acl.Authorizer) bool
|
|
|
|
CanWrite(acl.Authorizer) bool
|
2019-04-10 21:27:28 +00:00
|
|
|
|
2019-03-19 22:56:17 +00:00
|
|
|
GetRaftIndex() *RaftIndex
|
2019-03-19 17:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceConfiguration is the top-level struct for the configuration of a service
|
|
|
|
// across the entire cluster.
|
2019-03-19 22:56:17 +00:00
|
|
|
type ServiceConfigEntry struct {
|
2019-04-07 06:38:08 +00:00
|
|
|
Kind string
|
|
|
|
Name string
|
|
|
|
Protocol string
|
|
|
|
Connect ConnectConfiguration
|
2019-03-19 17:06:46 +00:00
|
|
|
|
|
|
|
RaftIndex
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ServiceConfigEntry) GetKind() string {
|
2019-03-19 17:06:46 +00:00
|
|
|
return ServiceDefaults
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ServiceConfigEntry) GetName() string {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
return e.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ServiceConfigEntry) Normalize() error {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return fmt.Errorf("config entry is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
e.Kind = ServiceDefaults
|
2019-03-27 23:52:38 +00:00
|
|
|
if e.Protocol == "" {
|
|
|
|
e.Protocol = DefaultServiceProtocol
|
|
|
|
} else {
|
|
|
|
e.Protocol = strings.ToLower(e.Protocol)
|
|
|
|
}
|
2019-03-22 16:25:37 +00:00
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ServiceConfigEntry) Validate() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-23 06:55:11 +00:00
|
|
|
func (e *ServiceConfigEntry) CanRead(rule acl.Authorizer) bool {
|
2019-04-10 21:27:28 +00:00
|
|
|
return rule.ServiceRead(e.Name)
|
|
|
|
}
|
|
|
|
|
2019-04-23 06:55:11 +00:00
|
|
|
func (e *ServiceConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
2019-04-10 21:27:28 +00:00
|
|
|
return rule.ServiceWrite(e.Name, nil)
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return &RaftIndex{}
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
return &e.RaftIndex
|
|
|
|
}
|
|
|
|
|
2019-03-19 17:06:46 +00:00
|
|
|
type ConnectConfiguration struct {
|
|
|
|
SidecarProxy bool
|
|
|
|
}
|
|
|
|
|
2019-03-19 22:56:17 +00:00
|
|
|
// ProxyConfigEntry is the top-level struct for global proxy configuration defaults.
|
|
|
|
type ProxyConfigEntry struct {
|
2019-03-27 23:52:38 +00:00
|
|
|
Kind string
|
|
|
|
Name string
|
|
|
|
Config map[string]interface{}
|
2019-03-19 22:56:17 +00:00
|
|
|
|
|
|
|
RaftIndex
|
2019-03-19 17:06:46 +00:00
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ProxyConfigEntry) GetKind() string {
|
2019-03-19 17:06:46 +00:00
|
|
|
return ProxyDefaults
|
|
|
|
}
|
2019-03-19 22:56:17 +00:00
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ProxyConfigEntry) GetName() string {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
return e.Name
|
2019-03-19 22:56:17 +00:00
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ProxyConfigEntry) Normalize() error {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return fmt.Errorf("config entry is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
e.Kind = ProxyDefaults
|
2019-04-07 06:38:08 +00:00
|
|
|
e.Name = ProxyConfigGlobal
|
2019-03-22 16:25:37 +00:00
|
|
|
|
2019-03-19 22:56:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ProxyConfigEntry) Validate() error {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return fmt.Errorf("config entry is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Name != ProxyConfigGlobal {
|
|
|
|
return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal)
|
|
|
|
}
|
|
|
|
|
2019-03-19 22:56:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-23 06:55:11 +00:00
|
|
|
func (e *ProxyConfigEntry) CanRead(rule acl.Authorizer) bool {
|
2019-04-10 21:27:28 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-04-23 06:55:11 +00:00
|
|
|
func (e *ProxyConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
2019-04-10 21:27:28 +00:00
|
|
|
return rule.OperatorWrite()
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
2019-03-22 16:25:37 +00:00
|
|
|
if e == nil {
|
|
|
|
return &RaftIndex{}
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:13:13 +00:00
|
|
|
return &e.RaftIndex
|
2019-03-19 22:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ConfigEntryOp string
|
|
|
|
|
|
|
|
const (
|
|
|
|
ConfigEntryUpsert ConfigEntryOp = "upsert"
|
|
|
|
ConfigEntryDelete ConfigEntryOp = "delete"
|
|
|
|
)
|
|
|
|
|
2019-04-07 06:38:08 +00:00
|
|
|
// ConfigEntryRequest is used when creating/updating/deleting a ConfigEntry.
|
2019-03-19 22:56:17 +00:00
|
|
|
type ConfigEntryRequest struct {
|
2019-04-07 06:38:08 +00:00
|
|
|
Op ConfigEntryOp
|
|
|
|
Datacenter string
|
|
|
|
Entry ConfigEntry
|
|
|
|
|
|
|
|
WriteRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigEntryRequest) RequestDatacenter() string {
|
|
|
|
return c.Datacenter
|
2019-03-19 22:56:17 +00:00
|
|
|
}
|
2019-03-28 06:56:35 +00:00
|
|
|
|
2019-04-07 06:38:08 +00:00
|
|
|
func (c *ConfigEntryRequest) MarshalBinary() (data []byte, err error) {
|
2019-03-28 06:56:35 +00:00
|
|
|
// bs will grow if needed but allocate enough to avoid reallocation in common
|
|
|
|
// case.
|
|
|
|
bs := make([]byte, 128)
|
|
|
|
enc := codec.NewEncoderBytes(&bs, msgpackHandle)
|
|
|
|
// Encode kind first
|
2019-04-07 06:38:08 +00:00
|
|
|
err = enc.Encode(c.Entry.GetKind())
|
2019-03-28 06:56:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Then actual value using alias trick to avoid infinite recursion
|
|
|
|
type Alias ConfigEntryRequest
|
|
|
|
err = enc.Encode(struct {
|
|
|
|
*Alias
|
|
|
|
}{
|
2019-04-07 06:38:08 +00:00
|
|
|
Alias: (*Alias)(c),
|
2019-03-28 06:56:35 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return bs, nil
|
|
|
|
}
|
|
|
|
|
2019-04-07 06:38:08 +00:00
|
|
|
func (c *ConfigEntryRequest) UnmarshalBinary(data []byte) error {
|
2019-03-28 06:56:35 +00:00
|
|
|
// First decode the kind prefix
|
|
|
|
var kind string
|
|
|
|
dec := codec.NewDecoderBytes(data, msgpackHandle)
|
|
|
|
if err := dec.Decode(&kind); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then decode the real thing with appropriate kind of ConfigEntry
|
2019-04-10 21:27:28 +00:00
|
|
|
entry, err := MakeConfigEntry(kind, "")
|
2019-04-02 22:42:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-04-07 06:38:08 +00:00
|
|
|
c.Entry = entry
|
2019-03-28 06:56:35 +00:00
|
|
|
|
|
|
|
// Alias juggling to prevent infinite recursive calls back to this decode
|
|
|
|
// method.
|
|
|
|
type Alias ConfigEntryRequest
|
|
|
|
as := struct {
|
|
|
|
*Alias
|
|
|
|
}{
|
2019-04-07 06:38:08 +00:00
|
|
|
Alias: (*Alias)(c),
|
2019-03-28 06:56:35 +00:00
|
|
|
}
|
|
|
|
if err := dec.Decode(&as); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-10 21:27:28 +00:00
|
|
|
func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
2019-03-28 06:56:35 +00:00
|
|
|
switch kind {
|
|
|
|
case ServiceDefaults:
|
2019-04-10 21:27:28 +00:00
|
|
|
return &ServiceConfigEntry{Name: name}, nil
|
2019-03-28 06:56:35 +00:00
|
|
|
case ProxyDefaults:
|
2019-04-10 21:27:28 +00:00
|
|
|
return &ProxyConfigEntry{Name: name}, nil
|
2019-03-28 06:56:35 +00:00
|
|
|
default:
|
2019-04-02 22:42:12 +00:00
|
|
|
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
2019-03-28 06:56:35 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-07 06:38:08 +00:00
|
|
|
|
|
|
|
// ConfigEntryQuery is used when requesting info about a config entry.
|
|
|
|
type ConfigEntryQuery struct {
|
|
|
|
Kind string
|
|
|
|
Name string
|
|
|
|
Datacenter string
|
|
|
|
|
|
|
|
QueryOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigEntryQuery) RequestDatacenter() string {
|
|
|
|
return c.Datacenter
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceConfigRequest is used when requesting the resolved configuration
|
|
|
|
// for a service.
|
|
|
|
type ServiceConfigRequest struct {
|
|
|
|
Name string
|
|
|
|
Datacenter string
|
|
|
|
|
|
|
|
QueryOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServiceConfigRequest) RequestDatacenter() string {
|
|
|
|
return s.Datacenter
|
|
|
|
}
|
|
|
|
|
2019-04-18 04:35:19 +00:00
|
|
|
func (r *ServiceConfigRequest) CacheInfo() cache.RequestInfo {
|
|
|
|
info := cache.RequestInfo{
|
|
|
|
Token: r.Token,
|
|
|
|
Datacenter: r.Datacenter,
|
|
|
|
MinIndex: r.MinQueryIndex,
|
|
|
|
Timeout: r.MaxQueryTime,
|
|
|
|
MaxAge: r.MaxAge,
|
|
|
|
MustRevalidate: r.MustRevalidate,
|
|
|
|
}
|
|
|
|
|
|
|
|
// To calculate the cache key we only hash the service name. The
|
|
|
|
// datacenter is handled by the cache framework. The other fields are
|
|
|
|
// not, but should not be used in any cache types.
|
|
|
|
v, err := hashstructure.Hash(r.Name, nil)
|
|
|
|
if err == nil {
|
|
|
|
// If there is an error, we don't set the key. A blank key forces
|
|
|
|
// no cache for this request so the request is forwarded directly
|
|
|
|
// to the server.
|
|
|
|
info.Key = strconv.FormatUint(v, 10)
|
|
|
|
}
|
|
|
|
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
2019-04-07 06:38:08 +00:00
|
|
|
type ServiceConfigResponse struct {
|
|
|
|
Definition ServiceDefinition
|
|
|
|
|
|
|
|
QueryMeta
|
|
|
|
}
|