diff --git a/streamhandler/stream_handler.go b/streamhandler/stream_handler.go index 9f664301..50ea68d9 100644 --- a/streamhandler/stream_handler.go +++ b/streamhandler/stream_handler.go @@ -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{ diff --git a/streamhandler/stream_handler_test.go b/streamhandler/stream_handler_test.go index c8146127..c787c52a 100644 --- a/streamhandler/stream_handler_test.go +++ b/streamhandler/stream_handler_test.go @@ -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: "", }, }, } diff --git a/tunnelhostnamemapper/tunnelhostnamemapper_test.go b/tunnelhostnamemapper/tunnelhostnamemapper_test.go index 685556fd..d8f8e655 100644 --- a/tunnelhostnamemapper/tunnelhostnamemapper_test.go +++ b/tunnelhostnamemapper/tunnelhostnamemapper_test.go @@ -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, } } diff --git a/tunnelrpc/pogs/config.go b/tunnelrpc/pogs/config.go index 1125d1d9..ac204484 100644 --- a/tunnelrpc/pogs/config.go +++ b/tunnelrpc/pogs/config.go @@ -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 { diff --git a/tunnelrpc/pogs/config_test.go b/tunnelrpc/pogs/config_test.go index ccc51d66..38ba5976 100644 --- a/tunnelrpc/pogs/config_test.go +++ b/tunnelrpc/pogs/config_test.go @@ -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 { diff --git a/tunnelrpc/pogs/json.go b/tunnelrpc/pogs/json.go deleted file mode 100644 index 497ee4d8..00000000 --- a/tunnelrpc/pogs/json.go +++ /dev/null @@ -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) -} diff --git a/tunnelrpc/pogs/json_test.go b/tunnelrpc/pogs/json_test.go deleted file mode 100644 index 70a896a4..00000000 --- a/tunnelrpc/pogs/json_test.go +++ /dev/null @@ -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() -} diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 5beecc18..d572ae8a 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -136,7 +136,7 @@ struct EdgeConnectionConfig { struct ReverseProxyConfig { tunnelHostname @0 :Text; - origin :union { + originConfig :union { http @1 :HTTPOriginConfig; websocket @2 :WebSocketOriginConfig; helloWorld @3 :HelloWorldOriginConfig; diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index 0dd6ab0e..87d92490 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -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\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'\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" + - "\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" + + "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,