feat: add access logging API to proxy defaults (#15780)

This commit is contained in:
Dan Stough 2022-12-13 14:52:18 -05:00 committed by GitHub
parent 04bf24c8c1
commit 233dbcb67f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package structs package structs
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -341,6 +342,7 @@ type ProxyConfigEntry struct {
TransparentProxy TransparentProxyConfig `json:",omitempty" alias:"transparent_proxy"` TransparentProxy TransparentProxyConfig `json:",omitempty" alias:"transparent_proxy"`
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"` MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
Expose ExposeConfig `json:",omitempty"` Expose ExposeConfig `json:",omitempty"`
AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"`
Meta map[string]string `json:",omitempty"` Meta map[string]string `json:",omitempty"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
@ -395,6 +397,32 @@ func (e *ProxyConfigEntry) Validate() error {
return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal) return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal)
} }
switch e.AccessLogs.Type {
case "", StdErrLogSinkType, StdOutLogSinkType:
// OK
case FileLogSinkType:
if e.AccessLogs.Path == "" {
return errors.New("path must be specified when using file type access logs")
}
default:
return fmt.Errorf("invalid access log type: %s", e.AccessLogs.Type)
}
if e.AccessLogs.JSONFormat != "" && e.AccessLogs.TextFormat != "" {
return errors.New("cannot specify both access log JSONFormat and TextFormat")
}
if e.AccessLogs.Type != FileLogSinkType && e.AccessLogs.Path != "" {
return errors.New("path is only valid for file type access logs")
}
if e.AccessLogs.JSONFormat != "" {
msg := json.RawMessage{}
if err := json.Unmarshal([]byte(e.AccessLogs.JSONFormat), &msg); err != nil {
return fmt.Errorf("invalid access log json for JSON format: %w", err)
}
}
if err := validateConfigEntryMeta(e.Meta); err != nil { if err := validateConfigEntryMeta(e.Meta); err != nil {
return err return err
} }

View File

@ -3030,6 +3030,57 @@ func TestProxyConfigEntry(t *testing.T) {
EnterpriseMeta: *acl.DefaultEnterpriseMeta(), EnterpriseMeta: *acl.DefaultEnterpriseMeta(),
}, },
}, },
"proxy config has invalid access log type": {
entry: &ProxyConfigEntry{
Name: "global",
AccessLogs: AccessLogsConfig{
Enabled: true,
Type: "stdin",
},
},
validateErr: "invalid access log type: stdin",
},
"proxy config has invalid access log config - both text and json formats": {
entry: &ProxyConfigEntry{
Name: "global",
AccessLogs: AccessLogsConfig{
Enabled: true,
JSONFormat: "[%START_TIME%]",
TextFormat: "{\"start_time\": \"[%START_TIME%]\"}",
},
},
validateErr: "cannot specify both access log JSONFormat and TextFormat",
},
"proxy config has invalid access log config - file path with wrong type": {
entry: &ProxyConfigEntry{
Name: "global",
AccessLogs: AccessLogsConfig{
Enabled: true,
Path: "/tmp/logs.txt",
},
},
validateErr: "path is only valid for file type access logs",
},
"proxy config has invalid access log config - no file path specified": {
entry: &ProxyConfigEntry{
Name: "global",
AccessLogs: AccessLogsConfig{
Enabled: true,
Type: FileLogSinkType,
},
},
validateErr: "path must be specified when using file type access logs",
},
"proxy config has invalid access log JSON format": {
entry: &ProxyConfigEntry{
Name: "global",
AccessLogs: AccessLogsConfig{
Enabled: true,
JSONFormat: "{\"start_time\": \"[%START_TIME%]\"", // Missing trailing brace
},
},
validateErr: "invalid access log json for JSON format",
},
} }
testConfigEntryNormalizeAndValidate(t, cases) testConfigEntryNormalizeAndValidate(t, cases)
} }

View File

@ -36,6 +36,14 @@ const (
MeshGatewayModeRemote MeshGatewayMode = "remote" MeshGatewayModeRemote MeshGatewayMode = "remote"
) )
type LogSinkType string
const (
FileLogSinkType LogSinkType = "file"
StdErrLogSinkType LogSinkType = "stderr"
StdOutLogSinkType LogSinkType = "stdout"
)
const ( const (
// TODO (freddy) Should we have a TopologySourceMixed when there is a mix of proxy reg and tproxy? // TODO (freddy) Should we have a TopologySourceMixed when there is a mix of proxy reg and tproxy?
// Currently we label as proxy-registration if ANY instance has the explicit upstream definition. // Currently we label as proxy-registration if ANY instance has the explicit upstream definition.
@ -143,6 +151,46 @@ func (c *TransparentProxyConfig) IsZero() bool {
return *c == zeroVal return *c == zeroVal
} }
// AccessLogsConfig contains the associated default settings for all Envoy instances within the datacenter or partition
type AccessLogsConfig struct {
// Enabled turns off all access logging
Enabled bool `json:",omitempty" alias:"enabled"`
// DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't
// have a matching listener filter.
DisableListenerLogs bool `json:",omitempty" alias:"disable_listener_logs"`
// Type selects the output for logs: "file", "stderr". "stdout"
Type LogSinkType `json:",omitempty" alias:"type"`
// Path is the output file to write logs
Path string `json:",omitempty" alias:"path"`
// The presence of one format string or the other implies the access log string encoding.
// Defining Both is invalid.
JSONFormat string `json:",omitempty" alias:"json_format"`
TextFormat string `json:",omitempty" alias:"text_format"`
}
func (c AccessLogsConfig) ToAPI() *api.AccessLogsConfig {
if c.IsZero() {
return nil
}
return &api.AccessLogsConfig{
Enabled: c.Enabled,
DisableListenerLogs: c.DisableListenerLogs,
Type: api.LogSinkType(c.Type),
Path: c.Path,
JSONFormat: c.JSONFormat,
TextFormat: c.TextFormat,
}
}
func (c *AccessLogsConfig) IsZero() bool {
zeroVal := AccessLogsConfig{}
return *c == zeroVal
}
// ConnectProxyConfig describes the configuration needed for any proxy managed // ConnectProxyConfig describes the configuration needed for any proxy managed
// or unmanaged. It describes a single logical service's listener and optionally // or unmanaged. It describes a single logical service's listener and optionally
// upstreams and sidecar-related config for a single instance. To describe a // upstreams and sidecar-related config for a single instance. To describe a

View File

@ -122,6 +122,35 @@ type ExposePath struct {
ParsedFromCheck bool ParsedFromCheck bool
} }
type LogSinkType string
const (
FileLogSinkType LogSinkType = "file"
StdErrLogSinkType LogSinkType = "stderr"
StdOutLogSinkType LogSinkType = "stdout"
)
// AccessLogsConfig contains the associated default settings for all Envoy instances within the datacenter or partition
type AccessLogsConfig struct {
// Enabled turns off all access logging
Enabled bool `json:",omitempty" alias:"enabled"`
// DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't
// have a matching listener filter.
DisableListenerLogs bool `json:",omitempty" alias:"disable_listener_logs"`
// Type selects the output for logs: "file", "stderr". "stdout"
Type LogSinkType `json:",omitempty" alias:"type"`
// Path is the output file to write logs
Path string `json:",omitempty" alias:"path"`
// The presence of one format string or the other implies the access log string encoding.
// Defining Both is invalid.
JSONFormat string `json:",omitempty" alias:"json_format"`
TextFormat string `json:",omitempty" alias:"text_format"`
}
type UpstreamConfiguration struct { type UpstreamConfiguration struct {
// Overrides is a slice of per-service configuration. The name field is // Overrides is a slice of per-service configuration. The name field is
// required. // required.
@ -266,6 +295,7 @@ type ProxyConfigEntry struct {
Config map[string]interface{} `json:",omitempty"` Config map[string]interface{} `json:",omitempty"`
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"` MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
Expose ExposeConfig `json:",omitempty"` Expose ExposeConfig `json:",omitempty"`
AccessLogs AccessLogsConfig `json:",omitempty"`
Meta map[string]string `json:",omitempty"` Meta map[string]string `json:",omitempty"`
CreateIndex uint64 CreateIndex uint64

View File

@ -401,6 +401,13 @@ func TestDecodeConfigEntry(t *testing.T) {
"TransparentProxy": { "TransparentProxy": {
"OutboundListenerPort": 808, "OutboundListenerPort": 808,
"DialedDirectly": true "DialedDirectly": true
},
"AccessLogs": {
"Enabled": true,
"DisableListenerLogs": true,
"Type": "file",
"Path": "/tmp/logs.txt",
"TextFormat": "[%START_TIME%]"
} }
} }
`, `,
@ -426,6 +433,13 @@ func TestDecodeConfigEntry(t *testing.T) {
OutboundListenerPort: 808, OutboundListenerPort: 808,
DialedDirectly: true, DialedDirectly: true,
}, },
AccessLogs: AccessLogsConfig{
Enabled: true,
DisableListenerLogs: true,
Type: FileLogSinkType,
Path: "/tmp/logs.txt",
TextFormat: "[%START_TIME%]",
},
}, },
}, },
{ {