TUN-2307: Capnp is the only serialization format used in tunnelpogs

This commit is contained in:
Chung-Ting Huang 2019-09-16 18:25:00 -05:00 committed by Chung Ting Huang
parent ff795a7beb
commit fe032843f3
9 changed files with 397 additions and 877 deletions

View File

@ -93,7 +93,7 @@ func (s *StreamHandler) UpdateConfig(newConfig []*pogs.ReverseProxyConfig) (fail
toAdd := s.tunnelHostnameMapper.ToAdd(newConfig)
for _, tunnelConfig := range toAdd {
tunnelHostname := tunnelConfig.TunnelHostname
originSerice, err := tunnelConfig.OriginConfigJSONHandler.OriginConfig.Service()
originSerice, err := tunnelConfig.OriginConfig.Service()
if err != nil {
s.logger.WithField("tunnelHostname", tunnelHostname).WithError(err).Error("Invalid origin service config")
failedConfigs = append(failedConfigs, &pogs.FailedConfig{

View File

@ -49,10 +49,8 @@ func TestServeRequest(t *testing.T) {
reverseProxyConfigs := []*pogs.ReverseProxyConfig{
{
TunnelHostname: testTunnelHostname,
OriginConfigJSONHandler: &pogs.OriginConfigJSONHandler{
OriginConfig: &pogs.HTTPOriginConfig{
URLString: httpServer.URL,
},
OriginConfig: &pogs.HTTPOriginConfig{
URLString: httpServer.URL,
},
},
}
@ -99,10 +97,8 @@ func TestServeBadRequest(t *testing.T) {
reverseProxyConfigs := []*pogs.ReverseProxyConfig{
{
TunnelHostname: testTunnelHostname,
OriginConfigJSONHandler: &pogs.OriginConfigJSONHandler{
OriginConfig: &pogs.HTTPOriginConfig{
URLString: "",
},
OriginConfig: &pogs.HTTPOriginConfig{
URLString: "",
},
},
}

View File

@ -193,20 +193,20 @@ func TestTunnelHostnameMapper_ToRemove(t *testing.T) {
func sampleConfig1() *pogs.ReverseProxyConfig {
return &pogs.ReverseProxyConfig{
TunnelHostname: "mock.example.com",
OriginConfigJSONHandler: &pogs.OriginConfigJSONHandler{OriginConfig: &pogs.HelloWorldOriginConfig{}},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
TunnelHostname: "mock.example.com",
OriginConfig: &pogs.HelloWorldOriginConfig{},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
}
}
func sampleConfig2() *pogs.ReverseProxyConfig {
return &pogs.ReverseProxyConfig{
TunnelHostname: "mock2.example.com",
OriginConfigJSONHandler: &pogs.OriginConfigJSONHandler{OriginConfig: &pogs.HelloWorldOriginConfig{}},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
TunnelHostname: "mock2.example.com",
OriginConfig: &pogs.HelloWorldOriginConfig{},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
}
}

View File

@ -4,7 +4,6 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"net"
"net/http"
@ -29,11 +28,43 @@ import (
// ClientConfig is a collection of FallibleConfig that determines how cloudflared should function
type ClientConfig struct {
Version Version `json:"version"`
SupervisorConfig *SupervisorConfig `json:"supervisor_config"`
EdgeConnectionConfig *EdgeConnectionConfig `json:"edge_connection_config"`
DoHProxyConfigs []*DoHProxyConfig `json:"doh_proxy_configs" capnp:"dohProxyConfigs"`
ReverseProxyConfigs []*ReverseProxyConfig `json:"reverse_proxy_configs"`
Version Version
SupervisorConfig *SupervisorConfig
EdgeConnectionConfig *EdgeConnectionConfig
DoHProxyConfigs []*DoHProxyConfig `capnp:"dohProxyConfigs"`
ReverseProxyConfigs []*ReverseProxyConfig
}
func (c *ClientConfig) MarshalBytes() ([]byte, error) {
msg, firstSeg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
return nil, err
}
capnpEntity, err := tunnelrpc.NewRootClientConfig(firstSeg)
if err != nil {
return nil, err
}
err = MarshalClientConfig(capnpEntity, c)
if err != nil {
return nil, err
}
return msg.Marshal()
}
func UnmarshalClientConfigFromBytes(clientConfigBytes []byte) (*ClientConfig, error) {
msg, err := capnp.Unmarshal(clientConfigBytes)
if err != nil {
return nil, err
}
capnpClientConfig, err := tunnelrpc.ReadRootClientConfig(msg)
if err != nil {
return nil, err
}
pogsClientConfig, err := UnmarshalClientConfig(capnpClientConfig)
if err != nil {
return nil, err
}
return pogsClientConfig, nil
}
// Version type models the version of a ClientConfig
@ -52,16 +83,17 @@ func (v Version) String() string {
}
// FallibleConfig is an interface implemented by configs that cloudflared might not be able to apply
//go-sumtype:decl FallibleConfig
type FallibleConfig interface {
FailReason(err error) string
jsonType() string
isFallibleConfig()
}
// SupervisorConfig specifies config of components managed by Supervisor other than ConnectionManager
type SupervisorConfig struct {
AutoUpdateFrequency time.Duration `json:"auto_update_frequency"`
MetricsUpdateFrequency time.Duration `json:"metrics_update_frequency"`
GracePeriod time.Duration `json:"grace_period"`
AutoUpdateFrequency time.Duration
MetricsUpdateFrequency time.Duration
GracePeriod time.Duration
}
// FailReason impelents FallibleConfig interface for SupervisorConfig
@ -69,23 +101,15 @@ func (sc *SupervisorConfig) FailReason(err error) string {
return fmt.Sprintf("Cannot apply SupervisorConfig, err: %v", err)
}
func (sc *SupervisorConfig) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]SupervisorConfig, 1)
marshaler[sc.jsonType()] = *sc
return json.Marshal(marshaler)
}
func (sc *SupervisorConfig) jsonType() string {
return "supervisor_config"
}
func (_ *SupervisorConfig) isFallibleConfig() {}
// EdgeConnectionConfig specifies what parameters and how may connections should ConnectionManager establish with edge
type EdgeConnectionConfig struct {
NumHAConnections uint8 `json:"num_ha_connections"`
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
Timeout time.Duration `json:"timeout"`
MaxFailedHeartbeats uint64 `json:"max_failed_heartbeats"`
UserCredentialPath string `json:"user_credential_path"`
NumHAConnections uint8
HeartbeatInterval time.Duration
Timeout time.Duration
MaxFailedHeartbeats uint64
UserCredentialPath string
}
// FailReason impelents FallibleConfig interface for EdgeConnectionConfig
@ -93,21 +117,13 @@ func (cmc *EdgeConnectionConfig) FailReason(err error) string {
return fmt.Sprintf("Cannot apply EdgeConnectionConfig, err: %v", err)
}
func (cmc *EdgeConnectionConfig) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]EdgeConnectionConfig, 1)
marshaler[cmc.jsonType()] = *cmc
return json.Marshal(marshaler)
}
func (cmc *EdgeConnectionConfig) jsonType() string {
return "edge_connection_config"
}
func (_ *EdgeConnectionConfig) isFallibleConfig() {}
// DoHProxyConfig is configuration for DNS over HTTPS service
type DoHProxyConfig struct {
ListenHost string `json:"listen_host"`
ListenPort uint16 `json:"listen_port"`
Upstreams []string `json:"upstreams"`
ListenHost string
ListenPort uint16
Upstreams []string
}
// FailReason impelents FallibleConfig interface for DoHProxyConfig
@ -115,23 +131,15 @@ func (dpc *DoHProxyConfig) FailReason(err error) string {
return fmt.Sprintf("Cannot apply DoHProxyConfig, err: %v", err)
}
func (dpc *DoHProxyConfig) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]DoHProxyConfig, 1)
marshaler[dpc.jsonType()] = *dpc
return json.Marshal(marshaler)
}
func (dpc *DoHProxyConfig) jsonType() string {
return "doh_proxy_config"
}
func (_ *DoHProxyConfig) isFallibleConfig() {}
// ReverseProxyConfig how and for what hostnames can this cloudflared proxy
type ReverseProxyConfig struct {
TunnelHostname h2mux.TunnelHostname `json:"tunnel_hostname"`
OriginConfigJSONHandler *OriginConfigJSONHandler `json:"origin_config"`
Retries uint64 `json:"retries"`
ConnectionTimeout time.Duration `json:"connection_timeout"`
CompressionQuality uint64 `json:"compression_quality"`
TunnelHostname h2mux.TunnelHostname
OriginConfig OriginConfig
Retries uint64
ConnectionTimeout time.Duration
CompressionQuality uint64
}
func NewReverseProxyConfig(
@ -145,11 +153,11 @@ func NewReverseProxyConfig(
return nil, fmt.Errorf("NewReverseProxyConfig: originConfigUnmarshaler was null")
}
return &ReverseProxyConfig{
TunnelHostname: h2mux.TunnelHostname(tunnelHostname),
OriginConfigJSONHandler: &OriginConfigJSONHandler{originConfig},
Retries: retries,
ConnectionTimeout: connectionTimeout,
CompressionQuality: compressionQuality,
TunnelHostname: h2mux.TunnelHostname(tunnelHostname),
OriginConfig: originConfig,
Retries: retries,
ConnectionTimeout: connectionTimeout,
CompressionQuality: compressionQuality,
}, nil
}
@ -158,58 +166,29 @@ func (rpc *ReverseProxyConfig) FailReason(err error) string {
return fmt.Sprintf("Cannot apply ReverseProxyConfig, err: %v", err)
}
func (rpc *ReverseProxyConfig) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]ReverseProxyConfig, 1)
marshaler[rpc.jsonType()] = *rpc
return json.Marshal(marshaler)
}
func (rpc *ReverseProxyConfig) jsonType() string {
return "reverse_proxy_config"
}
func (_ *ReverseProxyConfig) isFallibleConfig() {}
//go-sumtype:decl OriginConfig
type OriginConfig interface {
// Service returns a OriginService used to proxy to the origin
Service() (originservice.OriginService, error)
// go-sumtype requires at least one unexported method, otherwise it will complain that interface is not sealed
jsonType() string
}
type originType int
const (
httpType originType = iota
wsType
helloWorldType
)
func (ot originType) String() string {
switch ot {
case httpType:
return "Http"
case wsType:
return "WebSocket"
case helloWorldType:
return "HelloWorld"
default:
return "unknown"
}
isOriginConfig()
}
type HTTPOriginConfig struct {
URLString string `capnp:"urlString" json:"url_string" mapstructure:"url_string"`
TCPKeepAlive time.Duration `capnp:"tcpKeepAlive" json:"tcp_keep_alive" mapstructure:"tcp_keep_alive"`
DialDualStack bool `json:"dial_dual_stack" mapstructure:"dial_dual_stack"`
TLSHandshakeTimeout time.Duration `capnp:"tlsHandshakeTimeout" json:"tls_handshake_timeout" mapstructure:"tls_handshake_timeout"`
TLSVerify bool `capnp:"tlsVerify" json:"tls_verify" mapstructure:"tls_verify"`
OriginCAPool string `json:"origin_ca_pool" mapstructure:"origin_ca_pool"`
OriginServerName string `json:"origin_server_name" mapstructure:"origin_server_name"`
MaxIdleConnections uint64 `json:"max_idle_connections" mapstructure:"max_idle_connections"`
IdleConnectionTimeout time.Duration `json:"idle_connection_timeout" mapstructure:"idle_connection_timeout"`
ProxyConnectionTimeout time.Duration `json:"proxy_connection_timeout" mapstructure:"proxy_connection_timeout"`
ExpectContinueTimeout time.Duration `json:"expect_continue_timeout" mapstructure:"expect_continue_timeout"`
ChunkedEncoding bool `json:"chunked_encoding" mapstructure:"chunked_encoding"`
URLString string `capnp:"urlString"`
TCPKeepAlive time.Duration `capnp:"tcpKeepAlive"`
DialDualStack bool
TLSHandshakeTimeout time.Duration `capnp:"tlsHandshakeTimeout"`
TLSVerify bool `capnp:"tlsVerify"`
OriginCAPool string
OriginServerName string
MaxIdleConnections uint64
IdleConnectionTimeout time.Duration
ProxyConnectionTimeout time.Duration
ExpectContinueTimeout time.Duration
ChunkedEncoding bool
}
func (hc *HTTPOriginConfig) Service() (originservice.OriginService, error) {
@ -248,15 +227,13 @@ func (hc *HTTPOriginConfig) Service() (originservice.OriginService, error) {
return originservice.NewHTTPService(transport, url, hc.ChunkedEncoding), nil
}
func (_ *HTTPOriginConfig) jsonType() string {
return httpType.String()
}
func (*HTTPOriginConfig) isOriginConfig() {}
type WebSocketOriginConfig struct {
URLString string `capnp:"urlString" json:"url_string" mapstructure:"url_string"`
TLSVerify bool `capnp:"tlsVerify" json:"tls_verify" mapstructure:"tls_verify"`
OriginCAPool string `json:"origin_ca_pool" mapstructure:"origin_ca_pool"`
OriginServerName string `json:"origin_server_name" mapstructure:"origin_server_name"`
URLString string `capnp:"urlString"`
TLSVerify bool `capnp:"tlsVerify"`
OriginCAPool string
OriginServerName string
}
func (wsc *WebSocketOriginConfig) Service() (originservice.OriginService, error) {
@ -277,13 +254,11 @@ func (wsc *WebSocketOriginConfig) Service() (originservice.OriginService, error)
return originservice.NewWebSocketService(tlsConfig, url)
}
func (_ *WebSocketOriginConfig) jsonType() string {
return wsType.String()
}
func (*WebSocketOriginConfig) isOriginConfig() {}
type HelloWorldOriginConfig struct{}
func (_ *HelloWorldOriginConfig) Service() (originservice.OriginService, error) {
func (*HelloWorldOriginConfig) Service() (originservice.OriginService, error) {
helloCert, err := tlsconfig.GetHelloCertificateX509()
if err != nil {
return nil, errors.Wrap(err, "Cannot get Hello World server certificate")
@ -308,9 +283,7 @@ func (_ *HelloWorldOriginConfig) Service() (originservice.OriginService, error)
return originservice.NewHelloWorldService(transport)
}
func (_ *HelloWorldOriginConfig) jsonType() string {
return helloWorldType.String()
}
func (*HelloWorldOriginConfig) isOriginConfig() {}
/*
* Boilerplate to convert between these structs and the primitive structs
@ -519,9 +492,9 @@ func UnmarshalDoHProxyConfig(s tunnelrpc.DoHProxyConfig) (*DoHProxyConfig, error
func MarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig, p *ReverseProxyConfig) error {
s.SetTunnelHostname(p.TunnelHostname.String())
switch config := p.OriginConfigJSONHandler.OriginConfig.(type) {
switch config := p.OriginConfig.(type) {
case *HTTPOriginConfig:
ss, err := s.Origin().NewHttp()
ss, err := s.OriginConfig().NewHttp()
if err != nil {
return err
}
@ -529,7 +502,7 @@ func MarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig, p *ReverseProxyCo
return err
}
case *WebSocketOriginConfig:
ss, err := s.Origin().NewWebsocket()
ss, err := s.OriginConfig().NewWebsocket()
if err != nil {
return err
}
@ -537,7 +510,7 @@ func MarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig, p *ReverseProxyCo
return err
}
case *HelloWorldOriginConfig:
ss, err := s.Origin().NewHelloWorld()
ss, err := s.OriginConfig().NewHelloWorld()
if err != nil {
return err
}
@ -560,9 +533,9 @@ func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyC
return nil, err
}
p.TunnelHostname = h2mux.TunnelHostname(tunnelHostname)
switch s.Origin().Which() {
case tunnelrpc.ReverseProxyConfig_origin_Which_http:
ss, err := s.Origin().Http()
switch s.OriginConfig().Which() {
case tunnelrpc.ReverseProxyConfig_originConfig_Which_http:
ss, err := s.OriginConfig().Http()
if err != nil {
return nil, err
}
@ -570,9 +543,9 @@ func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyC
if err != nil {
return nil, err
}
p.OriginConfigJSONHandler = &OriginConfigJSONHandler{config}
case tunnelrpc.ReverseProxyConfig_origin_Which_websocket:
ss, err := s.Origin().Websocket()
p.OriginConfig = config
case tunnelrpc.ReverseProxyConfig_originConfig_Which_websocket:
ss, err := s.OriginConfig().Websocket()
if err != nil {
return nil, err
}
@ -580,9 +553,9 @@ func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyC
if err != nil {
return nil, err
}
p.OriginConfigJSONHandler = &OriginConfigJSONHandler{config}
case tunnelrpc.ReverseProxyConfig_origin_Which_helloWorld:
ss, err := s.Origin().HelloWorld()
p.OriginConfig = config
case tunnelrpc.ReverseProxyConfig_originConfig_Which_helloWorld:
ss, err := s.OriginConfig().HelloWorld()
if err != nil {
return nil, err
}
@ -590,7 +563,7 @@ func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyC
if err != nil {
return nil, err
}
p.OriginConfigJSONHandler = &OriginConfigJSONHandler{config}
p.OriginConfig = config
}
p.Retries = s.Retries()
p.ConnectionTimeout = time.Duration(s.ConnectionTimeout())
@ -690,13 +663,13 @@ func (i ClientService_PogsImpl) UseConfiguration(p tunnelrpc.ClientService_useCo
}
type UseConfigurationResult struct {
Success bool `json:"success"`
FailedConfigs []*FailedConfig `json:"failed_configs"`
Success bool
FailedConfigs []*FailedConfig
}
type FailedConfig struct {
Config FallibleConfig `json:"config"`
Reason string `json:"reason"`
Config FallibleConfig
Reason string
}
func MarshalFailedConfig(s tunnelrpc.FailedConfig, p *FailedConfig) error {

View File

@ -61,13 +61,13 @@ func ClientConfigTestCases() []*ClientConfig {
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
}),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleHTTPOriginConfig()}
c.OriginConfig = sampleHTTPOriginConfig()
}),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleHTTPOriginConfigUnixPath()}
c.OriginConfig = sampleHTTPOriginConfigUnixPath()
}),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleWebSocketOriginConfig()}
c.OriginConfig = sampleWebSocketOriginConfig()
}),
}
}
@ -83,21 +83,14 @@ func ClientConfigTestCases() []*ClientConfig {
}
func TestClientConfig(t *testing.T) {
for i, testCase := range ClientConfigTestCases() {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
capnpEntity, err := tunnelrpc.NewClientConfig(seg)
if !assert.NoError(t, err) {
t.Fatal("Couldn't initialize a new message")
}
err = MarshalClientConfig(capnpEntity, testCase)
if !assert.NoError(t, err, "testCase index %v failed to marshal", i) {
continue
}
result, err := UnmarshalClientConfig(capnpEntity)
if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) {
continue
}
assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i)
for _, testCase := range ClientConfigTestCases() {
b, err := testCase.MarshalBytes()
assert.NoError(t, err)
clientConfig, err := UnmarshalClientConfigFromBytes(b)
assert.NoError(t, err)
assert.Equal(t, testCase, clientConfig)
}
}
@ -167,13 +160,13 @@ func TestReverseProxyConfig(t *testing.T) {
testCases := []*ReverseProxyConfig{
sampleReverseProxyConfig(),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleHTTPOriginConfig()}
c.OriginConfig = sampleHTTPOriginConfig()
}),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleHTTPOriginConfigUnixPath()}
c.OriginConfig = sampleHTTPOriginConfigUnixPath()
}),
sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleWebSocketOriginConfig()}
c.OriginConfig = sampleWebSocketOriginConfig()
}),
}
for i, testCase := range testCases {
@ -323,11 +316,11 @@ func sampleDoHProxyConfig(overrides ...func(*DoHProxyConfig)) *DoHProxyConfig {
// applies any number of overrides to it, and returns it.
func sampleReverseProxyConfig(overrides ...func(*ReverseProxyConfig)) *ReverseProxyConfig {
sample := &ReverseProxyConfig{
TunnelHostname: "mock-non-lb-tunnel.example.com",
OriginConfigJSONHandler: &OriginConfigJSONHandler{&HelloWorldOriginConfig{}},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
TunnelHostname: "mock-non-lb-tunnel.example.com",
OriginConfig: &HelloWorldOriginConfig{},
Retries: 18,
ConnectionTimeout: 5 * time.Second,
CompressionQuality: 3,
}
sample.ensureNoZeroFields()
for _, f := range overrides {

View File

@ -1,101 +0,0 @@
package pogs
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
// ScopeUnmarshaler can marshal a Scope pog from JSON.
type ScopeUnmarshaler struct {
Scope Scope
}
// UnmarshalJSON takes in a JSON string, and attempts to marshal it into a Scope.
// If successful, the Scope member of this ScopeUnmarshaler is set and nil is returned.
// If unsuccessful, returns an error.
func (su *ScopeUnmarshaler) UnmarshalJSON(b []byte) error {
var scopeJSON map[string]interface{}
if err := json.Unmarshal(b, &scopeJSON); err != nil {
return errors.Wrapf(err, "cannot unmarshal %s into scopeJSON", string(b))
}
if group, ok := scopeJSON["group"]; ok {
if val, ok := group.(string); ok {
su.Scope = NewGroup(val)
return nil
}
return fmt.Errorf("JSON should have been a Scope, but the 'group' key contained %v", group)
}
if systemName, ok := scopeJSON["system_name"]; ok {
if val, ok := systemName.(string); ok {
su.Scope = NewSystemName(val)
return nil
}
return fmt.Errorf("JSON should have been a Scope, but the 'system_name' key contained %v", systemName)
}
return fmt.Errorf("JSON should have been an object with one root key, either 'system_name' or 'group'")
}
// OriginConfigJSONHandler is a wrapper to serialize OriginConfig with type information, and deserialize JSON
// into an OriginConfig.
type OriginConfigJSONHandler struct {
OriginConfig OriginConfig
}
func (ocjh *OriginConfigJSONHandler) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]OriginConfig, 1)
marshaler[ocjh.OriginConfig.jsonType()] = ocjh.OriginConfig
return json.Marshal(marshaler)
}
func (ocjh *OriginConfigJSONHandler) UnmarshalJSON(b []byte) error {
var originJSON map[string]interface{}
if err := json.Unmarshal(b, &originJSON); err != nil {
return errors.Wrapf(err, "cannot unmarshal %s into originJSON", string(b))
}
if originConfig, ok := originJSON[httpType.String()]; ok {
httpOriginConfig := &HTTPOriginConfig{}
if err := mapstructure.Decode(originConfig, httpOriginConfig); err != nil {
return errors.Wrapf(err, "cannot decode %+v into HTTPOriginConfig", originConfig)
}
ocjh.OriginConfig = httpOriginConfig
return nil
}
if originConfig, ok := originJSON[wsType.String()]; ok {
wsOriginConfig := &WebSocketOriginConfig{}
if err := mapstructure.Decode(originConfig, wsOriginConfig); err != nil {
return errors.Wrapf(err, "cannot decode %+v into WebSocketOriginConfig", originConfig)
}
ocjh.OriginConfig = wsOriginConfig
return nil
}
if originConfig, ok := originJSON[helloWorldType.String()]; ok {
helloWorldOriginConfig := &HelloWorldOriginConfig{}
if err := mapstructure.Decode(originConfig, helloWorldOriginConfig); err != nil {
return errors.Wrapf(err, "cannot decode %+v into HelloWorldOriginConfig", originConfig)
}
ocjh.OriginConfig = helloWorldOriginConfig
return nil
}
return fmt.Errorf("cannot unmarshal %s into OriginConfig", string(b))
}
// FallibleConfigMarshaler is a wrapper for FallibleConfig to implement custom marshal logic
type FallibleConfigMarshaler struct {
FallibleConfig FallibleConfig
}
func (fcm *FallibleConfigMarshaler) MarshalJSON() ([]byte, error) {
marshaler := make(map[string]FallibleConfig, 1)
marshaler[fcm.FallibleConfig.jsonType()] = fcm.FallibleConfig
return json.Marshal(marshaler)
}

View File

@ -1,342 +0,0 @@
package pogs
import (
"encoding/json"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestScopeUnmarshaler_UnmarshalJSON(t *testing.T) {
type fields struct {
Scope Scope
}
type args struct {
b []byte
}
tests := []struct {
name string
fields fields
args args
wantErr bool
wantScope Scope
}{
{
name: "group_successful",
args: args{b: []byte(`{"group": "my-group"}`)},
wantScope: NewGroup("my-group"),
},
{
name: "system_name_successful",
args: args{b: []byte(`{"system_name": "my-computer"}`)},
wantScope: NewSystemName("my-computer"),
},
{
name: "not_a_scope",
args: args{b: []byte(`{"x": "y"}`)},
wantErr: true,
},
{
name: "malformed_group",
args: args{b: []byte(`{"group": ["a", "b"]}`)},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
su := &ScopeUnmarshaler{
Scope: tt.fields.Scope,
}
err := su.UnmarshalJSON(tt.args.b)
if !tt.wantErr {
if err != nil {
t.Errorf("ScopeUnmarshaler.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
}
if !eqScope(tt.wantScope, su.Scope) {
t.Errorf("Wanted scope %v but got scope %v", tt.wantScope, su.Scope)
}
}
})
}
}
func TestUnmarshalOrigin(t *testing.T) {
tests := []struct {
jsonLiteral string
exceptedOriginConfig OriginConfig
}{
{
jsonLiteral: `{
"Http":{
"url_string":"https.example.com",
"tcp_keep_alive":7000000000,
"dial_dual_stack":true,
"tls_handshake_timeout":11000000000,
"tls_verify":true,
"origin_ca_pool":"/etc/cert.pem",
"origin_server_name":"secure.example.com",
"max_idle_connections":19,
"idle_connection_timeout":17000000000,
"proxy_connection_timeout":15000000000,
"expect_continue_timeout":21000000000,
"chunked_encoding":true
}
}`,
exceptedOriginConfig: sampleHTTPOriginConfig(),
},
{
jsonLiteral: `{
"WebSocket":{
"url_string":"ssh://example.com",
"tls_verify":true,
"origin_ca_pool":"/etc/cert.pem",
"origin_server_name":"secure.example.com"
}
}`,
exceptedOriginConfig: sampleWebSocketOriginConfig(),
},
{
jsonLiteral: `{
"HelloWorld": {}
}`,
exceptedOriginConfig: &HelloWorldOriginConfig{},
},
}
for _, test := range tests {
originConfigJSON := prettyToValidJSON(test.jsonLiteral)
var OriginConfigJSONHandler OriginConfigJSONHandler
err := json.Unmarshal([]byte(originConfigJSON), &OriginConfigJSONHandler)
assert.NoError(t, err)
assert.Equal(t, test.exceptedOriginConfig, OriginConfigJSONHandler.OriginConfig)
}
}
func TestUnmarshalClientConfig(t *testing.T) {
prettyClientConfigJSON := `{
"version":10,
"supervisor_config":{
"auto_update_frequency":86400000000000,
"metrics_update_frequency":300000000000,
"grace_period":30000000000
},
"edge_connection_config":{
"num_ha_connections":4,
"heartbeat_interval":5000000000,
"timeout":30000000000,
"max_failed_heartbeats":5,
"user_credential_path":"~/.cloudflared/cert.pem"
},
"doh_proxy_configs":[{
"listen_host": "localhost",
"listen_port": 53,
"upstreams": ["https://1.1.1.1/dns-query", "https://1.0.0.1/dns-query"]
}],
"reverse_proxy_configs":[{
"tunnel_hostname":"sdfjadk33.cftunnel.com",
"origin_config":{
"Http":{
"url_string":"https://127.0.0.1:8080",
"tcp_keep_alive":30000000000,
"dial_dual_stack":true,
"tls_handshake_timeout":10000000000,
"tls_verify":true,
"origin_ca_pool":"",
"origin_server_name":"",
"max_idle_connections":100,
"idle_connection_timeout":90000000000,
"proxy_connection_timeout":90000000000,
"expect_continue_timeout":90000000000,
"chunked_encoding":true
}
},
"retries":5,
"connection_timeout":30,
"compression_quality":0
}]
}`
// replace new line and tab
clientConfigJSON := prettyToValidJSON(prettyClientConfigJSON)
var clientConfig ClientConfig
err := json.Unmarshal([]byte(clientConfigJSON), &clientConfig)
assert.NoError(t, err)
assert.Equal(t, Version(10), clientConfig.Version)
supervisorConfig := SupervisorConfig{
AutoUpdateFrequency: time.Hour * 24,
MetricsUpdateFrequency: time.Second * 300,
GracePeriod: time.Second * 30,
}
assert.Equal(t, supervisorConfig, *clientConfig.SupervisorConfig)
edgeConnectionConfig := EdgeConnectionConfig{
NumHAConnections: 4,
HeartbeatInterval: time.Second * 5,
Timeout: time.Second * 30,
MaxFailedHeartbeats: 5,
UserCredentialPath: "~/.cloudflared/cert.pem",
}
assert.Equal(t, edgeConnectionConfig, *clientConfig.EdgeConnectionConfig)
dohProxyConfig := DoHProxyConfig{
ListenHost: "localhost",
ListenPort: 53,
Upstreams: []string{"https://1.1.1.1/dns-query", "https://1.0.0.1/dns-query"},
}
assert.Len(t, clientConfig.DoHProxyConfigs, 1)
assert.Equal(t, dohProxyConfig, *clientConfig.DoHProxyConfigs[0])
reverseProxyConfig := ReverseProxyConfig{
TunnelHostname: "sdfjadk33.cftunnel.com",
OriginConfigJSONHandler: &OriginConfigJSONHandler{
OriginConfig: &HTTPOriginConfig{
URLString: "https://127.0.0.1:8080",
TCPKeepAlive: time.Second * 30,
DialDualStack: true,
TLSHandshakeTimeout: time.Second * 10,
TLSVerify: true,
OriginCAPool: "",
OriginServerName: "",
MaxIdleConnections: 100,
IdleConnectionTimeout: time.Second * 90,
ProxyConnectionTimeout: time.Second * 90,
ExpectContinueTimeout: time.Second * 90,
ChunkedEncoding: true,
},
},
Retries: 5,
ConnectionTimeout: 30,
CompressionQuality: 0,
}
assert.Len(t, clientConfig.ReverseProxyConfigs, 1)
assert.Equal(t, reverseProxyConfig, *clientConfig.ReverseProxyConfigs[0])
}
func TestMarshalFallibleConfig(t *testing.T) {
tests := []struct {
fallibleConfig FallibleConfig
expctedJSONLiteral string
}{
{
fallibleConfig: sampleSupervisorConfig(),
expctedJSONLiteral: `{
"supervisor_config":{
"auto_update_frequency":75600000000000,
"metrics_update_frequency":660000000000,
"grace_period":31000000000
}
}`,
},
{
fallibleConfig: sampleEdgeConnectionConfig(),
expctedJSONLiteral: `{
"edge_connection_config":{
"num_ha_connections":49,
"heartbeat_interval":5000000000,
"timeout":9000000000,
"max_failed_heartbeats":9001,
"user_credential_path":"/Users/example/.cloudflared/cert.pem"
}
}`,
},
{
fallibleConfig: sampleDoHProxyConfig(),
expctedJSONLiteral: `{
"doh_proxy_config":{
"listen_host":"127.0.0.1",
"listen_port":53,
"upstreams":["1.1.1.1","1.0.0.1"]
}
}`,
},
{
fallibleConfig: sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleHTTPOriginConfig()}
}),
expctedJSONLiteral: `{
"reverse_proxy_config":{
"tunnel_hostname":"mock-non-lb-tunnel.example.com",
"origin_config":{
"Http":{
"url_string":"https.example.com",
"tcp_keep_alive":7000000000,
"dial_dual_stack":true,
"tls_handshake_timeout":11000000000,
"tls_verify":true,
"origin_ca_pool":"/etc/cert.pem",
"origin_server_name":"secure.example.com",
"max_idle_connections":19,
"idle_connection_timeout":17000000000,
"proxy_connection_timeout":15000000000,
"expect_continue_timeout":21000000000,
"chunked_encoding":true
}
},
"retries":18,
"connection_timeout":5000000000,
"compression_quality":3
}
}`,
},
{
fallibleConfig: sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{sampleWebSocketOriginConfig()}
}),
expctedJSONLiteral: `{
"reverse_proxy_config":{
"tunnel_hostname":"mock-non-lb-tunnel.example.com",
"origin_config":{
"WebSocket":{
"url_string":"ssh://example.com",
"tls_verify":true,
"origin_ca_pool":"/etc/cert.pem",
"origin_server_name":"secure.example.com"
}
},
"retries":18,
"connection_timeout":5000000000,
"compression_quality":3
}
}`,
},
{
fallibleConfig: sampleReverseProxyConfig(func(c *ReverseProxyConfig) {
c.OriginConfigJSONHandler = &OriginConfigJSONHandler{&HelloWorldOriginConfig{}}
}),
expctedJSONLiteral: `{
"reverse_proxy_config":{
"tunnel_hostname":"mock-non-lb-tunnel.example.com",
"origin_config":{
"HelloWorld":{}
},
"retries":18,
"connection_timeout":5000000000,
"compression_quality":3
}
}`,
},
}
for _, test := range tests {
b, err := json.Marshal(test.fallibleConfig)
assert.NoError(t, err)
assert.Equal(t, prettyToValidJSON(test.expctedJSONLiteral), string(b))
}
}
type prettyJSON string
func prettyToValidJSON(prettyJSON string) string {
return strings.ReplaceAll(strings.ReplaceAll(prettyJSON, "\n", ""), "\t", "")
}
func eqScope(s1, s2 Scope) bool {
return s1.Value() == s2.Value() && s1.PostgresType() == s2.PostgresType()
}

View File

@ -136,7 +136,7 @@ struct EdgeConnectionConfig {
struct ReverseProxyConfig {
tunnelHostname @0 :Text;
origin :union {
originConfig :union {
http @1 :HTTPOriginConfig;
websocket @2 :WebSocketOriginConfig;
helloWorld @3 :HelloWorldOriginConfig;

View File

@ -1374,27 +1374,27 @@ func (p EdgeConnectionConfig_Promise) Struct() (EdgeConnectionConfig, error) {
}
type ReverseProxyConfig struct{ capnp.Struct }
type ReverseProxyConfig_origin ReverseProxyConfig
type ReverseProxyConfig_origin_Which uint16
type ReverseProxyConfig_originConfig ReverseProxyConfig
type ReverseProxyConfig_originConfig_Which uint16
const (
ReverseProxyConfig_origin_Which_http ReverseProxyConfig_origin_Which = 0
ReverseProxyConfig_origin_Which_websocket ReverseProxyConfig_origin_Which = 1
ReverseProxyConfig_origin_Which_helloWorld ReverseProxyConfig_origin_Which = 2
ReverseProxyConfig_originConfig_Which_http ReverseProxyConfig_originConfig_Which = 0
ReverseProxyConfig_originConfig_Which_websocket ReverseProxyConfig_originConfig_Which = 1
ReverseProxyConfig_originConfig_Which_helloWorld ReverseProxyConfig_originConfig_Which = 2
)
func (w ReverseProxyConfig_origin_Which) String() string {
func (w ReverseProxyConfig_originConfig_Which) String() string {
const s = "httpwebsockethelloWorld"
switch w {
case ReverseProxyConfig_origin_Which_http:
case ReverseProxyConfig_originConfig_Which_http:
return s[0:4]
case ReverseProxyConfig_origin_Which_websocket:
case ReverseProxyConfig_originConfig_Which_websocket:
return s[4:13]
case ReverseProxyConfig_origin_Which_helloWorld:
case ReverseProxyConfig_originConfig_Which_helloWorld:
return s[13:23]
}
return "ReverseProxyConfig_origin_Which(" + strconv.FormatUint(uint64(w), 10) + ")"
return "ReverseProxyConfig_originConfig_Which(" + strconv.FormatUint(uint64(w), 10) + ")"
}
// ReverseProxyConfig_TypeID is the unique identifier for the type ReverseProxyConfig.
@ -1439,12 +1439,14 @@ func (s ReverseProxyConfig) SetTunnelHostname(v string) error {
return s.Struct.SetText(0, v)
}
func (s ReverseProxyConfig) Origin() ReverseProxyConfig_origin { return ReverseProxyConfig_origin(s) }
func (s ReverseProxyConfig_origin) Which() ReverseProxyConfig_origin_Which {
return ReverseProxyConfig_origin_Which(s.Struct.Uint16(0))
func (s ReverseProxyConfig) OriginConfig() ReverseProxyConfig_originConfig {
return ReverseProxyConfig_originConfig(s)
}
func (s ReverseProxyConfig_origin) Http() (HTTPOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) Which() ReverseProxyConfig_originConfig_Which {
return ReverseProxyConfig_originConfig_Which(s.Struct.Uint16(0))
}
func (s ReverseProxyConfig_originConfig) Http() (HTTPOriginConfig, error) {
if s.Struct.Uint16(0) != 0 {
panic("Which() != http")
}
@ -1452,7 +1454,7 @@ func (s ReverseProxyConfig_origin) Http() (HTTPOriginConfig, error) {
return HTTPOriginConfig{Struct: p.Struct()}, err
}
func (s ReverseProxyConfig_origin) HasHttp() bool {
func (s ReverseProxyConfig_originConfig) HasHttp() bool {
if s.Struct.Uint16(0) != 0 {
return false
}
@ -1460,14 +1462,14 @@ func (s ReverseProxyConfig_origin) HasHttp() bool {
return p.IsValid() || err != nil
}
func (s ReverseProxyConfig_origin) SetHttp(v HTTPOriginConfig) error {
func (s ReverseProxyConfig_originConfig) SetHttp(v HTTPOriginConfig) error {
s.Struct.SetUint16(0, 0)
return s.Struct.SetPtr(1, v.Struct.ToPtr())
}
// NewHttp sets the http field to a newly
// allocated HTTPOriginConfig struct, preferring placement in s's segment.
func (s ReverseProxyConfig_origin) NewHttp() (HTTPOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) NewHttp() (HTTPOriginConfig, error) {
s.Struct.SetUint16(0, 0)
ss, err := NewHTTPOriginConfig(s.Struct.Segment())
if err != nil {
@ -1477,7 +1479,7 @@ func (s ReverseProxyConfig_origin) NewHttp() (HTTPOriginConfig, error) {
return ss, err
}
func (s ReverseProxyConfig_origin) Websocket() (WebSocketOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) Websocket() (WebSocketOriginConfig, error) {
if s.Struct.Uint16(0) != 1 {
panic("Which() != websocket")
}
@ -1485,7 +1487,7 @@ func (s ReverseProxyConfig_origin) Websocket() (WebSocketOriginConfig, error) {
return WebSocketOriginConfig{Struct: p.Struct()}, err
}
func (s ReverseProxyConfig_origin) HasWebsocket() bool {
func (s ReverseProxyConfig_originConfig) HasWebsocket() bool {
if s.Struct.Uint16(0) != 1 {
return false
}
@ -1493,14 +1495,14 @@ func (s ReverseProxyConfig_origin) HasWebsocket() bool {
return p.IsValid() || err != nil
}
func (s ReverseProxyConfig_origin) SetWebsocket(v WebSocketOriginConfig) error {
func (s ReverseProxyConfig_originConfig) SetWebsocket(v WebSocketOriginConfig) error {
s.Struct.SetUint16(0, 1)
return s.Struct.SetPtr(1, v.Struct.ToPtr())
}
// NewWebsocket sets the websocket field to a newly
// allocated WebSocketOriginConfig struct, preferring placement in s's segment.
func (s ReverseProxyConfig_origin) NewWebsocket() (WebSocketOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) NewWebsocket() (WebSocketOriginConfig, error) {
s.Struct.SetUint16(0, 1)
ss, err := NewWebSocketOriginConfig(s.Struct.Segment())
if err != nil {
@ -1510,7 +1512,7 @@ func (s ReverseProxyConfig_origin) NewWebsocket() (WebSocketOriginConfig, error)
return ss, err
}
func (s ReverseProxyConfig_origin) HelloWorld() (HelloWorldOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) HelloWorld() (HelloWorldOriginConfig, error) {
if s.Struct.Uint16(0) != 2 {
panic("Which() != helloWorld")
}
@ -1518,7 +1520,7 @@ func (s ReverseProxyConfig_origin) HelloWorld() (HelloWorldOriginConfig, error)
return HelloWorldOriginConfig{Struct: p.Struct()}, err
}
func (s ReverseProxyConfig_origin) HasHelloWorld() bool {
func (s ReverseProxyConfig_originConfig) HasHelloWorld() bool {
if s.Struct.Uint16(0) != 2 {
return false
}
@ -1526,14 +1528,14 @@ func (s ReverseProxyConfig_origin) HasHelloWorld() bool {
return p.IsValid() || err != nil
}
func (s ReverseProxyConfig_origin) SetHelloWorld(v HelloWorldOriginConfig) error {
func (s ReverseProxyConfig_originConfig) SetHelloWorld(v HelloWorldOriginConfig) error {
s.Struct.SetUint16(0, 2)
return s.Struct.SetPtr(1, v.Struct.ToPtr())
}
// NewHelloWorld sets the helloWorld field to a newly
// allocated HelloWorldOriginConfig struct, preferring placement in s's segment.
func (s ReverseProxyConfig_origin) NewHelloWorld() (HelloWorldOriginConfig, error) {
func (s ReverseProxyConfig_originConfig) NewHelloWorld() (HelloWorldOriginConfig, error) {
s.Struct.SetUint16(0, 2)
ss, err := NewHelloWorldOriginConfig(s.Struct.Segment())
if err != nil {
@ -1597,27 +1599,27 @@ func (p ReverseProxyConfig_Promise) Struct() (ReverseProxyConfig, error) {
return ReverseProxyConfig{s}, err
}
func (p ReverseProxyConfig_Promise) Origin() ReverseProxyConfig_origin_Promise {
return ReverseProxyConfig_origin_Promise{p.Pipeline}
func (p ReverseProxyConfig_Promise) OriginConfig() ReverseProxyConfig_originConfig_Promise {
return ReverseProxyConfig_originConfig_Promise{p.Pipeline}
}
// ReverseProxyConfig_origin_Promise is a wrapper for a ReverseProxyConfig_origin promised by a client call.
type ReverseProxyConfig_origin_Promise struct{ *capnp.Pipeline }
// ReverseProxyConfig_originConfig_Promise is a wrapper for a ReverseProxyConfig_originConfig promised by a client call.
type ReverseProxyConfig_originConfig_Promise struct{ *capnp.Pipeline }
func (p ReverseProxyConfig_origin_Promise) Struct() (ReverseProxyConfig_origin, error) {
func (p ReverseProxyConfig_originConfig_Promise) Struct() (ReverseProxyConfig_originConfig, error) {
s, err := p.Pipeline.Struct()
return ReverseProxyConfig_origin{s}, err
return ReverseProxyConfig_originConfig{s}, err
}
func (p ReverseProxyConfig_origin_Promise) Http() HTTPOriginConfig_Promise {
func (p ReverseProxyConfig_originConfig_Promise) Http() HTTPOriginConfig_Promise {
return HTTPOriginConfig_Promise{Pipeline: p.Pipeline.GetPipeline(1)}
}
func (p ReverseProxyConfig_origin_Promise) Websocket() WebSocketOriginConfig_Promise {
func (p ReverseProxyConfig_originConfig_Promise) Websocket() WebSocketOriginConfig_Promise {
return WebSocketOriginConfig_Promise{Pipeline: p.Pipeline.GetPipeline(1)}
}
func (p ReverseProxyConfig_origin_Promise) HelloWorld() HelloWorldOriginConfig_Promise {
func (p ReverseProxyConfig_originConfig_Promise) HelloWorld() HelloWorldOriginConfig_Promise {
return HelloWorldOriginConfig_Promise{Pipeline: p.Pipeline.GetPipeline(1)}
}
@ -3704,229 +3706,228 @@ func (p ClientService_useConfiguration_Results_Promise) Result() UseConfiguratio
return UseConfigurationResult_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
}
const schema_db8274f9144abc7e = "x\xda\xacY}l]\xe5y\x7f\x9e\xf3^\xfb\xd8\x89" +
"o\xae\x0f\xe7\"b\x13\xcbU\x04jCI\x06\xf1\xb2" +
"\x11o\xeb\x8d\xed$\xb5\xdd|\xdc\xe3\x8f\x00!\x91r" +
"r\xef\xeb\xeb\xe3\x9c{\xce\xcd\xf9\x08v\x946\x90\x85" +
"\x01\x1e)\x09m&\x92\x86\x0a\xb2f|(]\x09\x0d" +
"\xda\xca\xa0*\xd3\x18d\xad\x04h0\xc1\xc6\xfeX!" +
"\x9a@\x8b\x18\xb4\x12\xa2*\x9c\xe99\xdf\xbe\xbeu\xc2" +
"\xd4\x7f\x92\xab\xe7<\xef\xfb>\x9f\xbf\xe7\xc37e[" +
"\xd7\x0977\x99Y\x00\xe5TS\xb3\xf7\xa7\xd5WO" +
"\xff\xd1\xf1\x9f\x1f\x06\xa9S\xf0\xbe\xf5\xfcp\xfeS\xe7" +
"\xd0\x7f\x00`\xcf#\xcd\xfbQ>\xd7,\x02\xc8?l" +
"\xde\x0a\xe8\xfd\xebM\x07\xde\xdb\xf5\xabc\xf7\x81\xd4\x89" +
"\x09gF\x04\xe8y\xa9y\x16\xe5\xffl\x16\x81y\x8f" +
"\xdc\x91\xff\x17|\xf4\x93c }\x05\x01\x9a\x90>\xff" +
"\xa4y\x91\x00(\xbf\xde\\\x00\xf4^\xbd\xf1\xf9\xe7\x8e" +
"\xfe\xf8\xde\xef\x81\xf2eD\x08\xce\x7f\xd4\xfc\x1b\x04\x94" +
"\x9bDb\xf8\xe8\x07_\xcd\xfc\xf0\xd5\xab\xbe\xef3x" +
"g^\xbf\xf5\x99\xa3?\xfe\xd2\x070.\x88\x98\x01\xe8" +
"Y)Z\xc4\xbbV\xfco@\xef\xbbo\xbd\xb0\xa5z" +
"\xec\xe4i\x90\xbe\x1c\xdd\xd5\xd1\"\x08\x90\xf1\xd6\xfc\xfb" +
"\xc5\xad\x9b\x9f\x99x<\xf8\x12\xc8\x91my\x86\x8ev" +
"\xb5\xd03\xaf\xdc\xd9\xfe@\xdf\x1f?\xf88(\x9d\x98" +
"\xd2\xa7\xa9\x898\xfbZfQ\xbe\xbd\x85~\x8e\xb7\xdc" +
"\x8a\x80\xde\xec\xda\x17\xb6\xfd\xea/\xec\xa7@Y\x89\x19" +
"\xef\x9f\xee\x7fw\xdf\x8a''^\xf6\xa5b\x00=\x17" +
"ZO\xd3\xd5o\xb7\xfe\x08\xd0\xcb\xfe\xc3\x0d[\x1e|" +
"o\xd39\xba:e\xd4@\x88\x99E\xbd(\xdf\xbf\x88" +
"\xecz\xcf\"\xe2~\xed\xc6m?\xfd\xe9\xd3\x95s\xf5" +
"\x82\x08\xc4}\xfd\xe2a\x94\xd7.&\xee5\x8b\x89\xfb" +
"\xea!|\xe7g7g\xfe.\xd4\x8b\x11\xd3\xfb\x8b?" +
"\xa0\xc7?\xf3\x19\xee\xf8\xed\xb3\xff\xb8\xe1\xc37~\x92" +
"v\xc0#m\x029\xe0\\\x1b)\xdeu\xa9?k|" +
"x\xe8gs\xfd\x18\xdc\xf4v\xdb0\xca\x97\xda\xe8\xb9" +
"\xf7\xdb\xe8\xb6\xbfr\xdfz\xc3\x1c\x1e~\xa9^8\xff" +
"\xda{\xb2\x02\xca\xc7\xb2\xc4}$[\x00\xfc\xe4\xa9{" +
"\x8f\x0e\xbd\xbb\xfee\xa5\x133\xf5\xbc\xc7\xb2\xfbQ>" +
"C\xbc=\x8fe\xbb\xc9\xa2\xb1\x0d\xeb\xd8}\xbd__" +
"2\x85\xf2\xc5%\xf4\xf3\xbf\x96\xf8\xec\xc3w|\xe7\xa1" +
"\xa6\x8b\xdfy\xb9\xde\xa8\xa2o\x81\x9c\x85\xf2g9\xfa" +
"\xf9i\xeeq\x01\xd0\xfb\xcd\xd9?\xe7\x17W\xbdr\x01" +
"\x94/aJ\x8dq\x14Q\x00\xe8\xb9x\xd5j2\xd9" +
"\xa5\xab\xee\x04\xf4:\x9f\xfe\x93\xbf\xed/\xbf\xfd\xf3:" +
"\x8b\x90 \xf2f\xf9c\xf9v\x99~\x8d\xcb\xc4{\xef" +
"Wg\xf6o\xb9~\xf6\xcd\x86\x069'\xcf\xa2|\xc1" +
"\xe7~\xc9\xe7\x16.\xaa\x1dw\xfd\xdb\xd7\xdeI\xc5\xe7" +
"\x9a\xfc/\x112\xde\x96mwL\xb5~\xf3\xddw\xd3" +
"\xf1\xb9\"\xef\xfb\xf1\xcf\xf2\xe4\xa6\xf3\xd2C\xf2\xf3\x8f" +
"\xfd\xcd{\xf4\x90X\xef\xa7\x9d\xf9\xed(\xef\xcd\xd3\xcf" +
"j\xde\xd77\xce\x93FQ\xa4]\xd3\x8b\xf2\xcc5$" +
"\x97{\x0d\xc9\xb5fW\x1f\xdfq\xcbm\x1f\x80\xd4\xc9" +
"\xe6d\xfd\xb3\xc4\xf9\x12q\xf6\xbcx\x8d\x88\xf2\xde\xa5" +
"\"\x80\xf7\xed\xca\xf6\x0b\x1f\x0d<\xf6\xbf\xf5\x97\xfb\x0a" +
"\xdd\xbe\xb4\x17e\x8d\xf8z\xf8R\xdfU=7\xff\xe5" +
"\xa5\xe3\x7f=\xf0\xd1\xbc\xdb\xcfu\xf4\xa3\xfcb\x07\xc9" +
"\xf1B\xc7\xd7\xe5K\x1d\xfe\xe5\xdfZ\xbfu\xed\xf2\x17" +
"?N[\xe2\xcd\x8e\x8f\xc9\x12\xefw\x90%&n\xf9" +
"\x9f\xaf_\xff\xed\x7f\xfe\xb8\xce=>ck\xe7\x0d(" +
"wt\xd2\x8dWw\x16\x00?\xdc\xf8\xfd7:s\x9d" +
"\xbfn$\xe8\xda\xce)\x947\x13o\xcfP\xa7/\xe8" +
"m\xbf<yg\xe1{\xbf\xfe\x84\xf4bu\x986s" +
"\xedv\x94\x8f\\K7\xdf\x7f-\xa5\xc2\xa6\xb3o\x7f" +
"m\xf2\xf8+\x9f\xd6\x1b\xc1w\xc8\x9ae\x87P\x1eZ" +
"F\xdc\x1b\x96\x112\x1d\xf8\xf0\xc4\xe0\x83;\xce~\x9e" +
"\xd6je\xd7s\xbe\x7f\xbbH\xab\xa9\xe3\x07\x9c\xc1\x87" +
"\x8fx\x8d\xd2pgW?\xca\xd5.\xbaM\xeb\xfa\x11" +
"\xac\xf4\x1c\xd70\xb8n\xd52\xa5?\x88~\x96V\x95" +
"\xd4\x9aQ\xeb\xdd0\xad\xd9\x8efT\xc6|z\xa1h" +
"\xeaZi\xa6\x88\xa8\xb4Q\xa0K]\xbd\x00\x88\xd2\xd5" +
"\xdb\x01P\x90\xa4~\x80\x82V1L\x8b{e\xcd." +
"\x99\x86\xc1\x81\x95\x9c\x83\xbbU]5J<~\xa8i" +
"\xfeC\x83\\\xd7\xcd[MK/o\xb5\xb4\x8af\x0c" +
"\x98\xc6\x84V\x01(\"\xc6\xc7\xc4\xf9\xc7\x06t\x8d\x1b" +
"\xce(\xb7\xf6i%\xbe\xca\xb5yp\xce\xb5TG3" +
"\x8d\xebF\xb8\xed\xea\x8e\x0d\xa0dX\x06 \x83\x00R" +
"\xb6\x17@ia\xa8\xe4\x05,X>\x03\xb6'\x99\x07" +
"\x88\xed\x90\xbc\xd9<\xff\xcd\xc0\x16\xf4&\xb7V\xb9\x86" +
"\xc5+\x9a\xedp+ _W(\xaa\x96Z\xb5\xd3\x0f" +
"\x9e\x04P\xda\x19*\xcb\x04\xf4*\x96Z\xe2En\xa1" +
"f\x96\xb7\xa8\x869\xcax\x09\x9b@\xc0\xa6\xd4\xa3\x0d" +
"\x1c\xb1Q\xd5t^\x0e\xb4[U\xea\xf6\xffW\xdaY" +
"\xa6\xcd\xf3\xfcG\xd4\xed\x00\xca.\x86\x8a.`\x16?" +
"\xf7\xf2T\xfe$m?\x802\xc9Pq\x04\xcc\x0a\x9f" +
"yy\xdfk{\x97\x03(:CeZ\xc0,\xfb\xad" +
"\x97\xa7\"#\xb9S\x00\x8a\xc3P\xb9K@\xcfvk" +
"dS\x1b\x98ia{\x12\xca\xa1ux\xb9B\x966" +
"\xa0\xc0Kdhl\x8f\xd09`\x10\xcb\xe6$\xb6'" +
"\xc5'<f\xf1}\xdc\xb2y\x11r\x969=\x83\xed" +
"\x09J\xd7Y=\xfbE\xad\x1e9:>\xb5\xf0y?" +
"4K\xceu\xc5\xeey\xce\";\xb61T\x96\x0a\xe8" +
"\xd5\xe8+w80\xcb\xc6\xf6\xa4\xa8\xd7I\xdb \x9c" +
"\x07\xe8\xdf\x81\xe0\x95bx\x8be\xfb\xe1\xac,\x8d\x1f" +
";A\x8f=\xccP\xf9\x81\x80\x12b\xe0\xb3\xc7,\x00" +
"\xe5Q\x86\xcaY\x01Q\x08<\xf6\xe4i\x00\xe5,C" +
"\xe5\xef\x05\x94\x98\x108\xec\xd9\x1b\x00\x94\xa7\x19*\xbf" +
"\x10P\xca\xb0<50\xd2\x05\x0a\xb6_0T\xde\x12" +
"Pj\xca\xe4\xb1\x09@zs5\x80\xf2\x1aC\xe5\x1d" +
"\x01=3\xc8/R\xca\xc1,\x08\x98\x05\xf4J\xba\xe9" +
"\x96't\x15\xba-^\x1eZ\x1f\xd3\x0d\xb7Z\xb4\xf8" +
">\x0dM\xd7\xees\x1c^\x15k\x8e\x8d\xcd `3" +
"`\xceQ+6.\x01,2\xc4\xf6\xa4\xd0\x01\x121" +
"\xbe\x13-^\xde\xc6-[c\xa6\x81m `\x1b`" +
"\xb7]2k\x1c\xdb\x93\xdayy\x9b\x8e\x84\xd1C\xb1" +
"\x13&\x82iibE3\x946\x96Y\xe6y\xa1\x01" +
"7\x90]\xd61T6\x09\xd8\x85\x9f\x13\x99l84" +
"\x02\xa0\x0c2T\xc6\x04\xec\x12>#2YQ!\x1f" +
"\x14\x19*;\x04\xccM:N\x0d\xdb\x93\xea\x18\x0au" +
"'\xdfm\x9b\xa5=\x1c\x90\xa0\"\x86\xea\xf0\xebd\x08" +
"]\xc0\xf42\xb6'\x9dl\x9dF\xacA\x94\xf8\x01R" +
"p6X\x96i\xf9\xa8\x1a\x87\xc6\x86\xd5\x89\x12Qd" +
"\x0cmO4\x90\x84u\x81Z\xca\xeeD\xfe\xee\x92\xea" +
"\xda<\xb2\xb1gq\xc7\x9a\xe9\x9bp\x80q+\xc6\x18" +
"{\xd2t\xf5\xf2\x08\x07\xd1\xb1f\x10A@\\\x18y" +
"\xd6\x9b\x83)\x93\x07!\x9c\x92\x93dZ\xcfP)&" +
"rn&\xda&\x86\xcam$gh\xfeq2\xff\x18" +
"C\xa5&\xa0\xa7S\xee\x1a\x83&0\xdb\x89\xc5\x0d\x88" +
"E\xd3\x0fL\x11\x04\x14\x01=\xb7f;\x16W\xab\x80" +
"q\xa4\x11\xff\x92/\x00\xd1uPQTs~\xce7" +
"\xd6!N\xc3\xcd\xc3i%\xc2<\x1c\xefO\x8c\xdd8" +
"\x91&M\xdb1\xd4*\x07\x80H\xb1\x83f\x8d0\x92" +
"\x10$n3\xebb\xe3\x8bW\xb6\xa0\xca\xcc\xa9k\xa7" +
"Se\xa6\x14\x9eF\xff\xf8\x80i\x88\x13Z\x05\xdb\x93" +
"^\xabN\x80\x06~\xefs\x9dIn8Z\xc9\x7fp" +
"\x9e\xdf\x97'\xf1\x19\xdblhu\xca\x90\x91\xcd6\xef" +
"N\x0c)\xee\xe131\x04\xf0\xaa\xaa\xe9\xb1\xf7Ck" +
"\xf6\x81\xf8\x8d\x84g\xa1\xe4\x19%\x04\xf1\xa5\xf2m\x80" +
"\xa9\xae\\\xca\xae\x06\xa1{\x9f\xaa\xbb|\xc1\xe6&\xac" +
"bA\x0d+\x04\x06\xa6\x0b\xf3\xb1\x9a\xdf\x9c\x05P\xee" +
"b\xa8<\x90R\xf3\xfe\x87\x00\x94\x07\x18*\x0f\xa7\xd4" +
"<N\xa1q\x94\xa1r\x8a \x9a\x05\xe0r\x82|r" +
"\x8a\xa1\xf2\x84\x80\x98\x09\x10\xfa\x0c!\xf4\x13\x0c\x95\xf3" +
"\x82\x8f\xaf\x83}\x03\xa6\x81\xa1\x106@\x84\xae\xde$" +
"W-g7W\xd1\x192\x1cn\xedSQ\x8f\xb2\xf8" +
"\xa0\xa3U\xb9\xe9:qVW\xd5i\xbfC\xc0\xf2`" +
"pJT\x1d\x1b[A\xc0VJ\"\x9b[\x03\x16/" +
"#\xf9S\xd5\x8b*s&\xe7\x998s9\xc4\xcd5" +
"0\x0f\xf5\x17\x07\x18*\xf7Q\xf6cj\x90\x95\xee\xe9" +
"\x05\xc1O~\xd2\xb9\xda\x9f\xf4!~\xfd\xa2\xaa\xb4\xf7" +
"\xa1\xa4\xe1\xf0\xebW3\xddx21x(\xda\xa0\x09" +
"\x85 \xa9\"\x99\x0bA\xb0\x1c$\x80\xd3x\xa2gX" +
"\xd654\x8d1\xdf@\x98X\xa8dVk\x16\xb7m" +
"\xd4LCqU]c\xceL|pA\x1b\x10z\x04" +
"Y\xb7\xb5\xd6\xed;\x89\x8cpSd\x04\xb9\x0f\x87\x01" +
"F\xd7!\xc3\xd1M\x98\x84\x89<\x84\xfd\x00\xa3\xeb\x89" +
"^\xc4$R\xe4\xcd\xd8\x090:H\xf41\x14\x10\x83" +
"X\x91\x15|\x0a`t\x8c\xc8\xbb0\xa9\xe8\xf2N\xff" +
"\xfa\x1dD\x9f\xc4\xa4\xa8\xcb\x1co\x00\x18\xddE\xf4\x03" +
"Do\x16|\x0b\xca38\x050:M\xf4\xc3D\x17" +
"\x9b\xf24\x1f\xc8w\xa3\x050z\x17\xd1\x1f z\xcb" +
"\xd2<\xb6\xd0\xfc\xe1\xd3\xef#\xfaw\x89\xde\xda\x91\xc7" +
"V\x00\xf9\x18\x1e\x02\x18=J\xf4SD_\x84y\\" +
"\x04 \x9f\xc0\x93\x00\xa3\xa7\x88\xfe\x04\xd1\x177\xe7q" +
"1\x80|\xc6\x97\xe7Q\xa2\x9f\xc5\x18\x82\x86\xcai$" +
"\xa4p\xd2\x92.\x80\x99v\x1c\x86<\x9c40\x80\xe9" +
"\xa2\x99\xa3Q\x03s\xc9\x0e\x09\x10s\x80^\xcd4\xf5" +
"-s\x11\xf6r\x8dH\x18\x16\x903\x8d\xa1r\x9c_" +
"A\x10m2\xa1\xbb\xa4\xeaC\xb5X\x12\xcd\xees\x1d" +
"\xd3\xadAwYux9\xae\x91\x96kl\xb4\xcc\xea" +
"\x18r\xab\xaa\x19\xaa\x0e\xf1\x97\x85b+\xe7\xbaZ\xf9" +
"\x8a\xf1lU\x80ZJK\xd2\xd9\xaf\xa0\xea\xf4\x15\x86" +
"\xca\x1f\xa6;\xfb\x9b\x09jod\xa8\xdcB\xdd\xfa\x8c" +
"\xed\xf0\xea\x16\x15X\x92#\xdd\x15\xcbtk\xf3\x1e\x16" +
"\xea\x1f\xee\xae\xf5\x8e\xa9~Z\xb7\xc4i\xbd\x82:\xa8" +
"\xeb\x18*7\xa5Po\xe5\xeaD\x8e\\:\x1b\x03\x91" +
"\xe7\xbd\xd4\xa0\x83\x1b\xaf\xabbA\xd7\x1e\x94\x96\xd4\xeb" +
"\xfd\xc9\xeb\xf1\xe3V\xa8\xee\xa0\x80\x07m\xb7T\"k" +
"G\xe6\x9f\x08g#\xe8\xa6\xbbS\x81\x10\xaf-\xc2@" +
"\xb8\xd2\x8e\xa1\xc2\x9d\xe0\xd7\x901aR\xa9\x15\xd5\xaa" +
"\xfd\xff<=\xc2\xed\x1c\x8d&\x97\x9d@\xe3E\xc4\xe5" +
"K\xf3\xe0\xd8X1\x19\x93Y\x80\xcai@\x1aI\x03" +
"R\x82GSi\xdc\x89:HY\xf1\x01\xa0H\xf4\x1d" +
"\x98\xcc\x17\xf2\xedxz\x0e\xf0d\xfa\x02@\xe2\xfe\xf5" +
"e\xa2\xd7|@\xc2\x00\x90\xaa\xfe\xfd:\xd1\xa7\xd3\x80" +
"\xe4\xe2\xec\\@b\x11 \x11\x90\x1c&\xfaQ\x1f\x90" +
"2\x01 \x1d\xc1g\xe6\x00OkS\x00H'\xf0\xb9" +
"9\xc0\xb3\xa89\x00\xa43>\xff\x13D?\xef\x03R" +
"\x7f\x00H\xe7|\x00{\x9a\xe8\xcf\x13 \xb9\x96>\xea" +
"X\x9a\x01XI\x82\xb5T\xfb\x06\xe7\xb5>\xc8\xe9\xda" +
">\x1e\x17\x8b\xb2\xa6\xea\xeb]U\x87\xeeQG-\xed" +
"I\xdad\xdd\x1eT\x8d\xb2\x8d\x93\xea\x1eN%FL" +
"\x17aG\xb7\xb7qK\x9b\x00L\x1a\xeb\xb8\xad\xc9\x15" +
"M\xb3\xbe\xdb\xf1\xfb3n\x05h\x16\x7f\xab\xaa\xd3C" +
"e\x9d\x0f`\xd4\x9a0#)q\x1a}1\x0d\x03\x83" +
"~aL\xeb\x9e\xdb\x08\xd4\xc2V=j(\xc6\x0au" +
"\x9d\x02\x9f\xae\xf1\x923`\xa2\xe1h\x86\xcb\xe7]P" +
"\x9at\x8d=\xbc\xbc\x01\x8d\x92Y\xd6\x8c\x0a\xcc\x9b\x11" +
"\xd8\xef\xdaN\xa4:\xa8\x96\xb0%\x8b\xd7\xed\xd2\x0a\xea" +
"\x070\xec\x07\xa4\xded\xca.\x94\xfcS\x05\x8b\xabv" +
"R\x1a\x16z-\\F\x05IF\xaf\xb5\xb3&\x80x" +
"y\x8d\xd1\xceO\xda\xbb\x1f\x04I\x131Y\x95b\xb4" +
"\x19\x95vZ H\xe3\"\x0a\xf1\xdf\x0c0\xda\xf7K" +
"C\xb3 H\x1bDd\xf1\xa2\x1f\xa3\x8d\x9b\xb4\xb6\x1f" +
"\x04i\xa5\xe8ES\x05\x14\x02q\xd6\xa1\x17%>t" +
"\xfb\xa9\xbf\x0e\xbdhO\x81\xd1\xf4\x01\xb0\x0e\x0f\x86\xf5" +
"h\x1d\xa6\x97[\xecw\x8d\x00\x8d\xfbR\xc2\xc8i\x86" +
"\xca\xe1\x04#\xef\xa6^\xf50C\xe5hj\xec:\xf2" +
"T\xba-\x0d7\x07'\x0e\x85{\x87\xf3\xa9\xcd\xc19" +
"\xeaU\xcf3T^\x13\x92B\x1d\x85]\xb4\x0fB\xd3" +
"\x8a\xe6\xc0\x05\xd6Bap\x86-c\xfdr\xc8+\x9b" +
"\x93~K\x89\xc1U6$\x88\x9d\xde\x18-Im\x8c" +
"0\x9a@\xc59\x00\x9f\xde\x1f-Y\x183\xe7\xccS" +
"\xe1\xd8@Q\x13\xfdE\x03\xa3\xbf-I\x12y?+" +
"z\xd1\xcc\x85Q\xb9\"\xe7\xa5]\xf6\x05\x07\xcf\x11\xde" +
"m_I%\x88\xb6\xcc\x97\xdf\x1f\x04\xef\xe4(\xd8\xe2" +
"9(\xb8w*\xb5\xc5\xd2\xcdp\x84\xcbmIU\xed" +
"\x85l\x15\x08\x1cu\xbe9:\\\x17~\xcb\x93\xf0\x8b" +
"\x1b\x84\xbb\x97'\xc3@<\x16\xdd3\x1c\x06\xe5\xa3q" +
"\xa7+=2\x9bl\xb8\xe2\xf0{r8\x19\x8bDn" +
"Y\x91\x9c\xa2k%\xb0\xa9\x9b\x95M\x9a\xc1m\xea\xfd" +
"\xea\xb6\x015nUU\x83\x1b\xe8\x10\x18\xb9\x16!\xea" +
"\\\xe4\x1aZ\x9fj\x19\x17R\x7f4\x0c\xf6 \xd6\xc3" +
"\xf2\x9a\x1a}O\xa7\xd60\x91\xf2\xcas\xe1zcW" +
"J\xf9\x9d4\xfa\xee`\xa8L\x0a\xe8\xa9\xaec\x8e\xd7" +
"\xca*:|\xa3\xc5\xf7\xba\\4J3\xc9\x00G\xa3" +
"L\xc9\x1e\xc7\x1a5\x9d\x1b-^\xd8\xeb\xf24C\xb4" +
"8\x06Q3\xcb\xf36\xc6\x0d\x9a\xad[\xf9\xeeQ\xb3" +
"\xb4\x87;s\x16\xea\x01\\F\xaa\xa8#\xc9\xd68\xd2" +
"D\x1bI\xcdj\x11\x8c\xec\xa5\x80\xaa1T\x0e\xa4`" +
"df6qx\xe3\xea\xfa\xfb)\x88\x0b(\xd9p\xab" +
";R\xe0W\x94h\xc9_I.\xdfr\x85{\x82\xb0" +
"c\xbd\x92]\xc8\x9ce]\x18\x10\xcaT\xb2?\xf2\x83" +
"\xbc=\xf9sn(\x83\x1d\xb6\x8e\xc0&\xcc\x06]a" +
"0U\x0d\x98\x10Bk\xfdF\xe7\xff\x02\x00\x00\xff\xff" +
"\xd4\xcdz3"
const schema_db8274f9144abc7e = "x\xda\xacY}p\x1c\xe5y\x7f\x9e}OZ\xc9\xd6" +
"\xf9n\xd9\xcb`\x09k\x94z\xc8$&\xd8\xc5Vi" +
"\xb1Zr\x96d;\x92\xe2\x8f[}\x18b\xcc\x8c\xd7" +
"w\xaf\xa4\x95\xf7vO\xfba$\x8f\x89\xb1\xeb\x14\xac" +
"B0$\xee`b2\xe0\xc6\xe5c\x9c\x06\x133-" +
"\x142i\xa7\x14\xdc$\x03\xb4\xd0!-\xfc\x13\xf0t" +
"\xca\x94\xa1&\xc90\xc9$l\xe7\xd9o\x9d\x0e\xd9\xee" +
"\xf4\x1f\xfb\xe6\xd9\xe7}\xdf\xe7\xf3\xf7|\xe8\x86\\\xeb" +
"\x06am\x93\x99\x05PN65{\x7fR}\xf5\xd4" +
"\x1f\x1e\xff\xf1\x11\x90:\x04\xefk/\x0c\x15~\xed\x1c" +
"\xfe\x0f\x00\xec~\xa4y?\xcag\x9bE\x00\xf9{\xcd" +
"\xdb\x01\xbd\x7f\xbd\xe1\xc0{\xbb\x7f\xf1\xc0= u`" +
"\xc2\x99\x11\x01\xba_j\x9eC\xf9\x9df\x11\x98\xf7\xc8" +
"m\x85\x7f\xc6G?~\x00\xa4/ @\x13\xd2\xe7\xe7" +
"\x9a\x97\x08\x80\xf2\xeb\xcdE@\xef\xd5\xeb_x\xfe\xd8" +
"\x0f\xee\xfe6(\x9fG\x84\xe0\xfc\xc5\xe6\xdf \xa0\xdc" +
"$\x12\xc3\xc5\xef~1\xf3\xbdW\xaf\xfa\x8e\xcf\xe0\x9d" +
"~\xfd\x96g\x8e\xfd\xe0\xb3\xef\xc3\x98 b\x06\xa0{" +
"\xb5h\x11\xefz\xf1?\x01\xbdo\xbd\xf5\xe2\xb6\xea\x03" +
"\x0f\x9f\x02\xe9\xf3\xd1]\xed-\x82\x00\x19\xef\xc6\x7f\xbf" +
"\xb0}\xeb3\xe3\x8f\x07_\x029\xb2-\xcf\xd0\xd1\xce" +
"\x16z\xe6\x95;\xf2\xf7\xf6\xfe\xd1\xfd\x8f\x83\xd2\x81)" +
"}\x9a\x9a\x88\xb3\xb7e\x0e\xe5\xaf\xb6\xd0\xcf\xb1\x96[" +
"\x10\xd0\x9b[\xff\xe2\x8e_\xfc\x99\xfd\x14(\xab1\xe3" +
"\xfd\xe3\xd1w\xf7\xadzr\xfce_*\x06\xd0}\xbe" +
"\xf5_\xe8\xeawZ\xbf\x0f\xe8e\xff\xee\xbam\xf7\xbf" +
"\xb7\xe5,]\x9d2j \xc4\x9dKzP\xbeo\x09" +
"\xd9\xf5\xe8\x12\xe2~\xed\xfa\x1d?\xfc\xe1\xd3\x13g\xeb" +
"\x05\x11\x88{\xd5\xd2!\x94o^J\xdc\xeb\x97\x12\xf7" +
"g\x06\xf1\xed\x1f\xad\xcd\xfcM\xa8\x17#\xa6\x0f\x96\xbe" +
"O\x8fc\x1b1\xdc\xf6\xdbg\xffa\xd3\x87o<\x97" +
"v\xc0cm\x029\xe0\xd96R\xbc\xf3\x83\xbe\xac\xf1" +
"\xe1\xe1\x1f\xcd\xf7cp\xd3;mC(_l\xa3\xe7" +
">\xf0o\xfb\x0b\xf7\xad7\xcc\xa1\xa1\x97\xea\x85\xf3\xaf" +
"=\x9a\x15P>\x9e%\xee\x07\xb2E\xc0\x8f\x9f\xba\xfb" +
"\xd8\xe0\xbb\x1b_V:0S\xcf{<\xbb\x1f\xe5'" +
"\x89\xb7\xfbt\xb6\x8b,\x1a\xdb\xb0\x8e\xdd\xd7\xfb\xcde" +
"S(\xff\xd72\xfaya\x99\xcf>t\xdb7\x1fl" +
"\xba\xf0\xcd\x97\xeb\x8d*\xfa\x01\x94\xb3Pn\xca\xd3O" +
"\xcc?.\x00z\xbf9\xf3\xa7\xfc\xc2\x9aW\xce\x83\xf2" +
"YL\xa91\x86\"\x0ad\xb2\xab\xd6\x91\xc9~u\xd5" +
"\x1d\x80^\xc7\xd3\x7f\xfc\xd7}\x95\x9f\xfd\xb8\xce\"$" +
"\x88<&\x7f$\xab2\xfd\xba]&\xde\xbb\xbf8\xbb" +
"\x7f\xdb\xe7\xe6\xdelh\x90\xe7\xe49\x94_\xf7\xb9\x7f" +
"\xeas\x0b\x17\xd4\xf6\xbb\xfe\xedKo\xa7\xe2\xf3\xe6\xc2" +
"\xcf\x112\xde\xb6\x1d\xb7M\xb5\xde\xf9\xee\xbb\xe9\xf8\\" +
"[\xf0\xfd\xb8\xa9@n:'=(\xbf\xf0\xd8_\xbd" +
"G\x0f\x89\xf5~\xe2\x85\x9d(\xcf\x16\xe8\xa7[\xf0\xf5" +
"\x8d\xf3\xa4Q\x14M_\xdd\x83\xf2\xa1\xabI\xae;\xaf" +
"&\xb9n\xdc\xdd\xcbw\xddt\xeb\xfb u\xb0yY" +
"\xff\"q\xfe\x948\xbb\xcf_-\xa2<\xbb\\\x04\xf0" +
"\xbe1\xb1\xf3\xfc\xc5\xfe\xc7\xfe\xa7\xfer_!uy" +
"\x0f\xca\xd3\xc4\xd7]]\xee\xbb\xaa{\xed\x9f\x7fp\xfc" +
"/\xfb/.\xb8\xfd\xb9\xf6>\x94\xcf\xb7\x93\x1c/\xb5" +
"\x7fY\xfeU\xbb\x7f\xf9\xd76n_\xbf\xf2\xef?J" +
"[\xe2\x9d\xf6\x8f\xc8\x12\x17\xdb\xc9\x12\xe37\xfd\xf7\x97" +
"?\xf7\x8d\x7f\xfa\xa8\xce=>\xa3\xd4q\x1d\xca\xbf\xd7" +
"A7vv\x14\x01?\xdc\xfc\x9d7:r\x1d\xbfl" +
"$ho\xc7\x14\xcac\xc4\xdb\xadt\xf8\x82\xde\xfa\xf3" +
"\x87\xef(~\xfb\x97\x1f\x93^\xac\x0e\xd3\x0e]\xb3\x13" +
"\xe5\xe3\xd7\xf8\xc1}\x0d\xa5\xc2\x963?\xfb\xd2\xe4\xf1" +
"W~]o\x04\xdf!7\xaf8\x8c\xb2\xb2\x82\xb8\xb7" +
"\xae d:\xf0\xe1\x89\x81\xfbw\x9d\xf9$\xad\xd5\x8d" +
"\x9d\xcf\xfb\xfe\xed$\xad\xa6\x8e\x1fp\x06\x1e\xba\xcfk" +
"\x94\x86\xbc\xb3\x0fe\xb7\x93n\x9b\xee\xfc>\xac\xf6\x1c" +
"\xd70\xb8n\xd52\xe5\xdf\x8f~\x96\xd7\x94\xd5\x9aQ" +
"\xeb\xd94\xa3\xd9\x8efL\x8c\xfa\xf4b\xc9\xd4\xb5\xf2" +
"l\x09Qi\xa3@\x97:{\x00\x10\xa5\xcf\xec\x04@" +
"A\x92\xfa\x00\x8a\xda\x84aZ\xdc\xabhv\xd94\x0c" +
"\x0e\xac\xec\x1c\xdc\xa3\xea\xaaQ\xe6\xf1CM\x0b\x1f\x1a" +
"\xe0\xban\xdebZze\xbb\xa5MhF\xbfi\x8c" +
"k\x13\x00%\xc4\xf8\x98\xb8\xf0X\xbf\xaeq\xc3\x19\xe1" +
"\xd6>\xad\xcc\xd7\xb86\x0f\xce\xb9\x96\xeah\xa6q\xed" +
"0\xb7]\xdd\xb1\x01\x94\x0c\xcb\x00d\x10@\xca\xf6\x00" +
"(-\x0c\x95\x82\x80E\xcbg\xc0|\x92y\x80\x98\x87" +
"\xe4\xcd\xe6\x85o\x06\xb6\xa07\xb9\xb5\xc65,>\xa1" +
"\xd9\x0e\xb7\x02\xf2\xb5\xc5\x92j\xa9U;\xfd\xe0\xc3\x00" +
"J\x9e\xa1\xb2B@o\xc2R\xcb\xbc\xc4-\xd4\xcc\xca" +
"6\xd50G\x18/c\x13\x08\xd8\x94z\xb4\x81#6" +
"\xab\x9a\xce+\x81vk\xca]\xfe\xffJ\x9ee\xda<" +
"\xcf\x7fD\xdd\x09\xa0\xecf\xa8\xe8\x02f\xf1\x13\xaf@" +
"\xe5O\xd2\xf6\x03(\x93\x0c\x15G\xc0\xac\xf0;\xaf\xe0" +
"{mz%\x80\xa23Tf\x04\xcc\xb2\xdfz\x05*" +
"2\x92;\x05\xa08\x0c\x95\xbb\x04\xf4l\xb7F6\xb5" +
"\x81\x99\x16\xe6\x93P\x0e\xad\xc3+\x13di\x03\x8a\xbc" +
"L\x86\xc6|\x84\xce\x01\x83X1'1\x9f\x14\x9f\xf0" +
"\x98\xc5\xf7q\xcb\xe6%\xc8Y\xe6\xcc,\xe6\x13\x94\xae" +
"\xb3z\xf6J\xad\x1e9:>\xb5\xf8y?4\xcb\xce" +
"\xb5\xa5\xae\x05\xce\";\xb61T\x96\x0b\xe8\xd5\xe8+" +
"w80\xcb\xc6|R\xd4\xeb\xa4m\x10\xce\xfd\xf4o" +
"\x7f\xf0J)\xbc\xc5\xb2\xfdpV\x96\xc7\x8f\x9d\xa0\xc7" +
"\x1eb\xa8|W@\x091\xf0\xd9c\x16\x80\xf2(C" +
"\xe5\x8c\x80(\x04\x1e{\xf2\x14\x80r\x86\xa1\xf2\xb7\x02" +
"JL\x08\x1c\xf6\xecu\x00\xca\xd3\x0c\x95\x9f\x08(e" +
"X\x81\x1a\x18\xe9<\x05\xdbO\x18*o\x09(5e" +
"\x0a\xd8\x04 \xbd\xb9\x0e@y\x8d\xa1\xf2\xb6\x80\x9e\x19" +
"\xe4\x17)\xe5`\x16\x04\xcc\x02ze\xddt+\xe3\xba" +
"\x0a]\x16\xaf\x0cn\x8c\xe9\x86[-Y|\x9f\x86\xa6" +
"k\xf7:\x0e\xaf\x8a5\xc7\xc6f\x10\xb0\x190\xe7\xa8" +
"\x136.\x03,1\xc4|R\xe8\x00\x89\x18\xdf\x89\x16" +
"\xaf\xec\xe0\x96\xad1\xd3\xc06\x10\xb0\x0d\xb0\xcb.\x9b" +
"5\x8e\xf9\xa4v^:\xef\x86\xc3\xe8\xa1\xd8\x09\x13\xc1" +
"L\xb0\x02'\x946\x96Y\xe1y\xa1\x197\x91u6" +
"0T\xb6\x08\xd8\x89\x9f\x10\x99,98\x0c\xa0\x0c0" +
"TF\x05\xec\x14~Gd\xb2\xa5B\x9e(1Tv" +
"\x09\x98\x9bt\x9c\x1a\xe6\x93\x1a\x19\x8av\x07\xdfc\x9b" +
"\xe5\xbd\x1c\x90\x00#\x06\xec\xf0\xebd\x08`\xc0\xf4\x0a" +
"\xe6\x93~\xb6N/\xd6 V\xfc0):\x9b,\xcb" +
"\xb4|l\x8d\x03d\xd3\xbaD\x89(>\x06w&\x1a" +
"H\xc2\x86@-eO\"\x7fWYum\x1eY\xda" +
"\xb3\xb8c\xcd\xf6\x8e;\xc0\xb8\x15#\x8d=i\xbaz" +
"e\x98\x83\xe8X\xb3\x88 .\x8e?\x1b\xcd\x81\x94" +
"\xe1\x83@N\xc9I2md\xa8\x94\x129\xb7\x12m" +
"\x0bC\xe5V\x9234\xff\x18\x99\x7f\x94\xa1R\x13\xd0" +
"\xd3)\x83\x8d\x01\x13\x98\xed\xc4\xe2\x06\xc4\x92\xe9\x87\xa7" +
"\x08\x02\x8a\x80\x9e[\xb3\x1d\x8b\xabU\xc08\xde\x88\x7f" +
"\xd9\x15\x00u\x1d`\x94\xd4\x9c\x9f\xf9\x8du\x88\x93q" +
"\xebPZ\x890\x1b\xc7\xfa\x12c7N\xa7I\xd3v" +
"\x0c\xb5\xca\x01 R\xec\xa0Y#\xa4$\x1c\x89\x9b\xcd" +
"\xba\xd8\xb8\xf2\xfa\x16\xd4\x9ay\xd5\xedT\xaa\xd8\x94\xc3" +
"\xd3\xe8\x1f\xef7\x0dq\\\x9b\xc0|\xd2q\xd5\x09\xd0" +
"\xc0\xef\xbd\xae3\xc9\x0dG+\xfb\x0f.\xf0\xfb\xca$" +
">c\x9b\x0d\xaeK\x192\xb2\xd9\xd6=\x89!\xc5\xbd" +
"|6\x06\x02^U5=\xf6~h\xcd^\x10\xbf\x92" +
"\xf0,\x96<#\x84#\xbeT\xbe\x0d0\xd5\x9bK\xd9" +
"u t\xedSu\x97/\xda\xe2\x84\xb5,\xa8d\xc5" +
"\xc0\xc0ta!V\xf3\xce9\x00\xe5.\x86\xca\xbd)" +
"5\x8f>\x08\xa0\xdc\xcbPy(\xa5\xe6q\x0a\x8dc" +
"\x0c\x95\x93\x04\xd4,\x00\x97\x13\xe4\x93\x93\x0c\x95'\x04" +
"\xc4L\x80\xd3\xa7\x09\xa7\x9f`\xa8\x9c\x13|\x94\x1d\xe8" +
"\xed7\x0d\x0c\x85\xb0\x01\"\x8c\xf5&\xb9j9{\xb8" +
"\x8a\xce\xa0\xe1pk\x9f\x8az\x94\xc5\x07\x1d\xad\xcaM" +
"\xd7\x89\xb3\xba\xaa\xce\xf8}\x02V\x06\x82S\xa2\xea\xd8" +
"\xd8\x0a\x02\xb6R\x12\xd9\xdc\xea\xb7x\x05\xc9\x9f\xaa^" +
"R\x993\xb9\xc0\xc4\x99K\xe1n\xae\x81y\xa8\xcb8" +
"\xc0P\xb9\x87\xb2\x1fS\xe3\xac\xf4\xf5)\x10\xfc\xe4'" +
"\x9d\xa7\xfb\x92\xbe\xc3\xafbT\x9b\\2\xe3\x0cC\xe5" +
"HX\xc5\x9a\x01\xa4Cd\x9d#\x0c\x95cB$\xda" +
"\x80\x09\xc5 \xa9\xea\x83\xc5\x04_\xa6\x83\x04t\x1aO" +
"\xf4\x0d\x8b\xbc\x86\xa61\xea\x1b\x0a\x13K\x95\xcdj\xcd" +
"\xe2\xb6\x8d\x9ai(\xae\xaak\xcc\x99\x8d\x0f.j\x0b" +
"B\x91 \xfb\xb6\xd7\xba|g\x911n\x88\x8c!\xf7" +
"\xe2\x10\xc0\xc8\x06d8\xb2\x05\x93p\x91\x07\xb1\x0f`" +
"d#\xd1K\x98D\x8c\xbc\x15;\x00F\x06\x88>\x8a" +
"\x02b\x103\xb2\x82O\x01\x8c\x8c\x12y7&\xf5]" +
"\xbe\xdd\xbf~\x17\xd1'1)\xf12\xc7\xeb\x00Fv" +
"\x13\xfd\x00\xd1\x9b\x05\xdf\x92\xf2,N\x01\x8c\xcc\x10\xfd" +
"\x08\xd1\xc5\xa6\x02M\x0b\xf2!\xb4\x00F\xee\"\xfa\xbd" +
"DoY^\xc0\x16\x00\xf9\xa8O\xbf\x87\xe8\xdf\"z" +
"k{\x01[iJ\xc1\xc3\x00#\xc7\x88~\x92\xe8K" +
"\xb0\x80K\x00\xe4\x13\xf80\xc0\xc8I\xa2?A\xf4\xa5" +
"\xcd\x05\\\x0a \x9f\xf6\xe5y\x94\xe8g0\x86\xa2\xc1" +
"J\x1a\x11)\xac\xb4\xa4'`\xa6\x1d\xbb\x96\x87s\x07" +
"\x06p]2s4x`.\xd9(\x01b\x0e\xd0\xab" +
"\x99\xa6\xbem>\xd2^\xaa-\x09\xc3\x02r\xa61X" +
"\x89\xf3,\x08\xa6-&t\x95U}\xb0\x16K\xa2\xd9" +
"\xbd\xaec\xba5\xe8\xaa\xa8\x0e\xaf\xc4\xb5\xd2r\x8d\xcd" +
"\x96Y\x1dEnU5C\xd5!\xfe\xb2Xl\xe5\\" +
"W\xab\\6\xae\xad\x09\xd0KiI\xfa\xfcUT\xa5" +
"\xbe\xc0P\xf9\x83t\x9f\xbf\x96 \xf7z\x86\xcaM\xd4" +
"\xbb\xcf\xda\x0e\xafnS\x81%\xb9\xd25a\x99nm" +
"\xc1\xc3B\xfd\xc3]\xb5\x9eQ\xd5O\xef\x968\xbdW" +
"Q'u-C\xe5\x86\x14\xfa\xad^\x97\xc8\x91Kg" +
"e \xf2\x82\x97\x1a\xf4\xc8cu\xd5,\xe8\xe1\x83\x12" +
"\x93z\xbd/y=~\xdc\x0a\xd5\x1d\x10\xf0\xa0\xed\x96" +
"\xcbd\xed\xc8\xfc\xe3\xe1\xa4\x04]tw*\x10\xe2%" +
"F\x18\x08\x97\xdb9Lp'\xf85h\x8c\x9bTr" +
"E\xb5j\xff\x1fO\x0fs;G\x83\xca%\xe7\xd1x" +
"-q\xe9\x12=0:ZJ\x86f\x16\xa0s\x1a\x90" +
"\x86\xd3\x80\x94\xe0\xd1T\x1aw\xa2NRV|\x00(" +
"\x11}\x17&\xd3\x86\xfcU<5\x0fx2\xbd\x01 " +
"q\xff\xfa\x0a\xd1k> a\x00HU\xff~\x9d\xe8" +
"3i@rqn> \xb1\x08\x90\x08H\x8e\x10\xfd" +
"\x98\x0fH\x99\x00\x90\xee\xc3g\xe6\x01OkS\x00H" +
"'\xf0\xf9y\xc0\xb3\xa49\x00\xa4\xd3>\xff\x13D?" +
"\xe7\x03R_\x00Hg}\x00{\x9a\xe8/\x10 \xb9" +
"\x96>\xe2X\x9a\x018\x91\x04k\xb9\xf6\x15\xcek\xbd" +
"\x90\xd3\xb5}<.\x16\x15M\xd57\xba\xaa\x0e]#" +
"\x8eZ\xde\x9b\xb4\xcb\xba=\xa0\x1a\x15\x1b'\xd5\xbd\x9c" +
"J\x8c\x98.\xc6\x8en\xef\xe0\x966\x0e\x984\xd8q" +
"{\x93+\x99f}\xd7\xe3\xf7i\xdc\x0a\xd0,\xfeV" +
"Ug\x06+:\xef\xc7\xa8EaFR\xe24\xfab" +
"\x1a\x06\x06}\xc3\xa8\xd65\xbf!\xa8\x85-{\xd4X" +
"\x8c\x16\xeb:\x06>S\xe3e\xa7\xdfD\xc3\xd1\x0c\x97" +
"/\xb8\xa0<\xe9\x1a{ye\x13\x1ae\xb3\xa2\x19\x13" +
"\xb0`V`\x9f\xb6\xabHuR-ak\x16/\xdf" +
"\xa5U= \xf8XB}\x81\xd4\x93\xcc\xdc\xc5\xb2\x7f" +
"\xaahq\xd5NJ\xc3b\xaf\x85\xab\xa9 \xc9\xe8\xb5" +
"<k\x02\x88W\xd9\x18m\x00\xa5\xe9\xfd H\x9a\x88" +
"\xc9\xe2\x14\xa3=\xa9t\xbb\x05\x824&\xa2\x10\xff\x05" +
"\x01\xa3\xed\xbf48\x07\x82\xb4ID\x16\xaf\xfd1\xda" +
"\xbfI\xeb\xfb@\x90V\x8b^4]@1\x10g\x03" +
"zQ\xe2C\x97\x9f\xfa\x1b\xd0\x8b\xb6\x16\x18M!\x00" +
"\x1b\xf0`X\x8f6`z\xd5\xc5>m\x14h\xdc\x9f" +
"\xf6%=T\x84\x91\x87\xe6\x92\x16*\x1e\xbf\xee{*" +
"\xdd\x9e\x86{\x84\x13\x87\xc3-\xc4\xb9\xd4\x1e\xe1,\xf5" +
"\xac\xe7\x18*\xaf\x09I\xa1\x8e\xc2.\xda\x0e\xa1iE" +
"\xf3\xe0\"K\xa208\xc3\xd6\xb1~U\xe4U\xccI" +
"\xbf\xb5\xc4\xe0*\x1b\x12\xc4N\xef\x8f\x96\xa5\xf6G\x18" +
"M\xa2\xe2<\x80Oo\x93\x96-\x8e\x99\xf3\xe6\xaap" +
"|\xa0\xa8\x89\xfe\xbe\x81\xd1_\x9a$\x89\xbc\x9f\x15\xbd" +
"h\xf6\xc2\xa8\\\x91\xf3\xd2.\xbb\xc2\x01t\x98w\xd9" +
"\x97S\x09\xa2\x9d\xf3\xa5\xf7\x08\xc1;9\x0a\xb6x\x1e" +
"\x0a\xee\x9dJ\xed\xb4t3\x1c\xe5r\xdb\xd2\xbd\xf4\"" +
"\xb6\x0a\x04\x8e:\xdf\x1c\x1d\xae\x0b\xbf\x95\xa9\x16>\x8e" +
"\xbf\x95\xc9P\x10\x8fG_\x1f\x0a\x83\xf2\xd1\xb8\xd3\x95" +
"\x1e\x99K\xf6]q\xf8=9\x94\x8cG\"\xb7\xacH" +
"N\xd1\xb5\x12\xd8\xd4\xcd\x89-\x9a\xc1m\xea\xfd\xea\xb6" +
"\x025nUU\x83\x1b\xe8\x10\x18\xb9\x16!\xea|\xe4" +
"\x1a\xdc\x98j\x19\x17S\x7f$\x0c\xf6 \xd6\xc3\xf2\x9a" +
"\x1a\x81O\xa5\xd61\x91\xf2\xca\xf3\xe1\x9acwJ\xf9" +
"\xdbi\x04\xde\xc5P\x99\x14\xd0S]\xc7\x1c\xabUT" +
"t\xf8f\x8bO\xbb\\4\xca\xb3\xc9 G\xa3L\xd9" +
"\x1e\xc3\x1a5\x9d\x9b-^\x9cvy\x9a!Z#\x83" +
"\xa8\x99\x95\x05\xfb\xe3\x06\xcd\xd6-|\xcf\x88Y\xde\xcb" +
"\x9dy\xeb\xf5\x00.#U\xd4\xe1d\x87\x1ci\xa2\x0d" +
"'\x1b\xe4\x18F\xa6)\xa0j\x0c\x95\x03)\x18\x99\x9d" +
"K\x1c\xde\xb8\xba\xfe\xff\x14\xc4E\x94l\xb8\xe3\x1d." +
"\xf2\xcbJ\xb4\xe4o&\x97n\xb9\xc2}A\xd8\xb1^" +
"\xceNd\xde\xd2.\x0c\x08e*\xd9#\xf9A\x9eO" +
"\xfe\xb8\x1b\xca`\x87\xad#\xb0q\xb3AW\x18LU" +
"\xd1\x04\xbcp\xb3\xf3\xbf\x01\x00\x00\xff\xff\x82\xd9\x7f\x1e"
func init() {
schemas.Register(schema_db8274f9144abc7e,