Release Argo Tunnel Client 2018.4.7

This commit is contained in:
cloudflare-warp-bot 2018-04-26 14:57:32 +00:00
parent ff6a14283d
commit 77e7c240c0
18 changed files with 460 additions and 293 deletions

View File

@ -103,7 +103,7 @@ func hello(c *cli.Context) error {
}
func startHelloWorldServer(listener net.Listener, shutdownC <-chan struct{}) error {
Log.Infof("Starting Hello World server at %s", listener.Addr())
logger.Infof("Starting Hello World server at %s", listener.Addr())
serverName := defaultServerName
if hostname, err := os.Hostname(); err == nil {
serverName = hostname

View File

@ -90,8 +90,8 @@ var sysvTemplate = ServiceTemplate{
# Short-Description: Argo Tunnel
# Description: Argo Tunnel agent
### END INIT INFO
cmd="{{.Path}} --config /etc/cloudflared/config.yml --origincert /etc/cloudflared/cert.pem --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s"
name=$(basename $(readlink -f $0))
cmd="{{.Path}} --config /etc/cloudflared/config.yml --origincert /etc/cloudflared/cert.pem --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
@ -184,17 +184,17 @@ func installLinuxService(c *cli.Context) error {
defaultConfigDir := filepath.Dir(c.String("config"))
defaultConfigFile := filepath.Base(c.String("config"))
if err = copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile); err != nil {
Log.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
logger.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
serviceConfigDir, credentialFile, defaultConfigFiles[0])
return err
}
switch {
case isSystemd():
Log.Infof("Using Systemd")
logger.Infof("Using Systemd")
return installSystemd(&templateArgs)
default:
Log.Infof("Using Sysv")
logger.Infof("Using Sysv")
return installSysv(&templateArgs)
}
}
@ -203,30 +203,30 @@ func installSystemd(templateArgs *ServiceTemplateArgs) error {
for _, serviceTemplate := range systemdTemplates {
err := serviceTemplate.Generate(templateArgs)
if err != nil {
Log.WithError(err).Infof("error generating service template")
logger.WithError(err).Infof("error generating service template")
return err
}
}
if err := runCommand("systemctl", "enable", "cloudflared.service"); err != nil {
Log.WithError(err).Infof("systemctl enable cloudflared.service error")
logger.WithError(err).Infof("systemctl enable cloudflared.service error")
return err
}
if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil {
Log.WithError(err).Infof("systemctl start cloudflared-update.timer error")
logger.WithError(err).Infof("systemctl start cloudflared-update.timer error")
return err
}
Log.Infof("systemctl daemon-reload")
logger.Infof("systemctl daemon-reload")
return runCommand("systemctl", "daemon-reload")
}
func installSysv(templateArgs *ServiceTemplateArgs) error {
confPath, err := sysvTemplate.ResolvePath()
if err != nil {
Log.WithError(err).Infof("error resolving system path")
logger.WithError(err).Infof("error resolving system path")
return err
}
if err := sysvTemplate.Generate(templateArgs); err != nil {
Log.WithError(err).Infof("error generating system template")
logger.WithError(err).Infof("error generating system template")
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
@ -245,36 +245,36 @@ func installSysv(templateArgs *ServiceTemplateArgs) error {
func uninstallLinuxService(c *cli.Context) error {
switch {
case isSystemd():
Log.Infof("Using Systemd")
logger.Infof("Using Systemd")
return uninstallSystemd()
default:
Log.Infof("Using Sysv")
logger.Infof("Using Sysv")
return uninstallSysv()
}
}
func uninstallSystemd() error {
if err := runCommand("systemctl", "disable", "cloudflared.service"); err != nil {
Log.WithError(err).Infof("systemctl disable cloudflared.service error")
logger.WithError(err).Infof("systemctl disable cloudflared.service error")
return err
}
if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil {
Log.WithError(err).Infof("systemctl stop cloudflared-update.timer error")
logger.WithError(err).Infof("systemctl stop cloudflared-update.timer error")
return err
}
for _, serviceTemplate := range systemdTemplates {
if err := serviceTemplate.Remove(); err != nil {
Log.WithError(err).Infof("error removing service template")
logger.WithError(err).Infof("error removing service template")
return err
}
}
Log.Infof("Successfully uninstall cloudflared service")
logger.Infof("Successfully uninstall cloudflared service")
return nil
}
func uninstallSysv() error {
if err := sysvTemplate.Remove(); err != nil {
Log.WithError(err).Infof("error removing service template")
logger.WithError(err).Infof("error removing service template")
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
@ -287,6 +287,6 @@ func uninstallSysv() error {
continue
}
}
Log.Infof("Successfully uninstall cloudflared service")
logger.Infof("Successfully uninstall cloudflared service")
return nil
}

View File

@ -136,7 +136,7 @@ func download(certURL, filePath string) bool {
return true
}
if err != nil {
Log.WithError(err).Error("Error fetching certificate")
logger.WithError(err).Error("Error fetching certificate")
return false
}
}
@ -179,16 +179,16 @@ func putSuccess(client *http.Client, certURL string) {
// indicate success to the relay server
req, err := http.NewRequest("PUT", certURL+"/ok", nil)
if err != nil {
Log.WithError(err).Error("HTTP request error")
logger.WithError(err).Error("HTTP request error")
return
}
resp, err := client.Do(req)
if err != nil {
Log.WithError(err).Error("HTTP error")
logger.WithError(err).Error("HTTP error")
return
}
resp.Body.Close()
if resp.StatusCode != 200 {
Log.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
logger.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
}
}

View File

@ -9,7 +9,9 @@ import (
"gopkg.in/urfave/cli.v2"
)
const launchAgentIdentifier = "com.cloudflare.cloudflared"
const (
launchdIdentifier = "com.cloudflare.cloudflared"
)
func runApp(app *cli.App) {
app.Commands = append(app.Commands, &cli.Command{
@ -32,7 +34,7 @@ func runApp(app *cli.App) {
}
var launchdTemplate = ServiceTemplate{
Path: installPath(launchAgentIdentifier),
Path: installPath(),
Content: fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -46,9 +48,9 @@ var launchdTemplate = ServiceTemplate{
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/%s.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/%s.err.log</string>
<string>%s</string>
<key>StandardErrorPath</key>
<string>%s</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
@ -57,54 +59,84 @@ var launchdTemplate = ServiceTemplate{
<key>ThrottleInterval</key>
<integer>20</integer>
</dict>
</plist>`, launchAgentIdentifier, launchAgentIdentifier, launchAgentIdentifier),
</plist>`, launchdIdentifier, stdoutPath(), stderrPath()),
}
func installPath(launchAgentIdentifier string) string {
pathPattern := "~/Library/LaunchAgents/%s.plist"
func isRootUser() bool {
return os.Geteuid() == 0
}
func installPath() string {
// User is root, use /Library/LaunchDaemons instead of home directory
if os.Geteuid() == 0 {
pathPattern = "/Library/LaunchDaemons/%s.plist"
if isRootUser() {
return fmt.Sprintf("/Library/LaunchDaemons/%s.plist", launchdIdentifier)
}
return fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", userHomeDir(), launchdIdentifier)
}
return fmt.Sprintf(pathPattern, launchAgentIdentifier)
func stdoutPath() string {
if isRootUser() {
return fmt.Sprintf("/Library/Logs/%s.out.log", launchdIdentifier)
}
return fmt.Sprintf("%s/Library/Logs/%s.out.log", userHomeDir(), launchdIdentifier)
}
func stderrPath() string {
if isRootUser() {
return fmt.Sprintf("/Library/Logs/%s.err.log", launchdIdentifier)
}
return fmt.Sprintf("%s/Library/Logs/%s.err.log", userHomeDir(), launchdIdentifier)
}
func installLaunchd(c *cli.Context) error {
Log.Infof("Installing Argo Tunnel as an user launch agent")
if isRootUser() {
logger.Infof("Installing Argo Tunnel client as a system launch daemon. " +
"Argo Tunnel client will run at boot")
} else {
logger.Infof("Installing Argo Tunnel client as an user launch agent. " +
"Note that Argo Tunnel client will only run when the user is logged in. " +
"If you want to run Argo Tunnel client at boot, install with root permission. " +
"For more information, visit https://developers.cloudflare.com/argo-tunnel/reference/service/")
}
etPath, err := os.Executable()
if err != nil {
Log.WithError(err).Infof("error determining executable path")
logger.WithError(err).Infof("error determining executable path")
return fmt.Errorf("error determining executable path: %v", err)
}
templateArgs := ServiceTemplateArgs{Path: etPath}
err = launchdTemplate.Generate(&templateArgs)
if err != nil {
Log.WithError(err).Infof("error generating launchd template")
logger.WithError(err).Infof("error generating launchd template")
return err
}
plistPath, err := launchdTemplate.ResolvePath()
if err != nil {
Log.WithError(err).Infof("error resolving launchd template path")
logger.WithError(err).Infof("error resolving launchd template path")
return err
}
Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
logger.Infof("Outputs are logged to %s and %s", stderrPath(), stdoutPath())
return runCommand("launchctl", "load", plistPath)
}
func uninstallLaunchd(c *cli.Context) error {
Log.Infof("Uninstalling Argo Tunnel as an user launch agent")
if isRootUser() {
logger.Infof("Uninstalling Argo Tunnel as a system launch daemon")
} else {
logger.Infof("Uninstalling Argo Tunnel as an user launch agent")
}
plistPath, err := launchdTemplate.ResolvePath()
if err != nil {
Log.WithError(err).Infof("error resolving launchd template path")
logger.WithError(err).Infof("error resolving launchd template path")
return err
}
err = runCommand("launchctl", "unload", plistPath)
if err != nil {
Log.WithError(err).Infof("error unloading")
logger.WithError(err).Infof("error unloading")
return err
}
Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
logger.Infof("Outputs are logged to %s and %s", stderrPath(), stdoutPath())
return launchdTemplate.Remove()
}

View File

@ -11,12 +11,12 @@ import (
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/cloudflare/cloudflared/log"
"github.com/cloudflare/cloudflared/metrics"
"github.com/cloudflare/cloudflared/origin"
"github.com/cloudflare/cloudflared/tlsconfig"
@ -48,7 +48,7 @@ const (
var listeners = gracenet.Net{}
var Version = "DEV"
var BuildTime = "unknown"
var Log *logrus.Logger
var logger = log.CreateLogger()
var defaultConfigFiles = []string{"config.yml", "config.yaml"}
// Launchd doesn't set root env variables, so there is default
@ -59,15 +59,6 @@ var defaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/clou
// May be closed by the Windows service runner.
var shutdownC chan struct{}
type BuildAndRuntimeInfo struct {
GoOS string `json:"go_os"`
GoVersion string `json:"go_version"`
GoArch string `json:"go_arch"`
WarpVersion string `json:"warp_version"`
WarpFlags map[string]interface{} `json:"warp_flags"`
WarpEnvs map[string]string `json:"warp_envs"`
}
func main() {
metrics.RegisterBuildInfo(BuildTime, Version)
raven.SetDSN(sentryDSN)
@ -306,18 +297,17 @@ func main() {
return nil
}
app.Before = func(context *cli.Context) error {
Log = logrus.New()
inputSource, err := findInputSourceContext(context)
if err != nil {
Log.WithError(err).Infof("Cannot load configuration from %s", context.String("config"))
logger.WithError(err).Infof("Cannot load configuration from %s", context.String("config"))
return err
} else if inputSource != nil {
err := altsrc.ApplyInputSourceValues(context, inputSource, app.Flags)
if err != nil {
Log.WithError(err).Infof("Cannot apply configuration from %s", context.String("config"))
logger.WithError(err).Infof("Cannot apply configuration from %s", context.String("config"))
return err
}
Log.Infof("Applied configuration from %s", context.String("config"))
logger.Infof("Applied configuration from %s", context.String("config"))
}
return nil
}
@ -404,45 +394,50 @@ func startServer(c *cli.Context) {
// c.NumFlags() == 0 && c.NArg() == 0. For cloudflared to work, the user needs to at
// least provide a hostname.
if c.NumFlags() == 0 && c.NArg() == 0 && os.Getenv("TUNNEL_HOSTNAME") == "" {
Log.Infof("No arguments were provided. You need to at least specify the hostname for this tunnel. See %s", quickStartUrl)
logger.Infof("No arguments were provided. You need to at least specify the hostname for this tunnel. See %s", quickStartUrl)
cli.ShowAppHelp(c)
return
}
logLevel, err := logrus.ParseLevel(c.String("loglevel"))
if err != nil {
Log.WithError(err).Fatal("Unknown logging level specified")
logger.WithError(err).Fatal("Unknown logging level specified")
}
Log.SetLevel(logLevel)
logger.SetLevel(logLevel)
protoLogLevel, err := logrus.ParseLevel(c.String("proto-loglevel"))
if err != nil {
Log.WithError(err).Fatal("Unknown protocol logging level specified")
logger.WithError(err).Fatal("Unknown protocol logging level specified")
}
protoLogger := logrus.New()
protoLogger.Level = protoLogLevel
if c.String("logfile") != "" {
if err := initLogFile(c, protoLogger); err != nil {
Log.Error(err)
logger.Error(err)
}
}
buildInfo := origin.GetBuildInfo()
logger.Infof("Build info: %+v", *buildInfo)
logger.Infof("Version %s", Version)
logClientOptions(c)
if c.IsSet("proxy-dns") {
port := c.Int("proxy-dns-port")
if port <= 0 || port > 65535 {
Log.Fatal("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
logger.Fatal("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
}
wg.Add(1)
listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"))
if err != nil {
close(dnsReadySignal)
listener.Stop()
Log.WithError(err).Fatal("Cannot create the DNS over HTTPS proxy server")
logger.WithError(err).Fatal("Cannot create the DNS over HTTPS proxy server")
}
go func() {
err := listener.Start(dnsReadySignal)
if err != nil {
Log.WithError(err).Fatal("Cannot start the DNS over HTTPS proxy server")
logger.WithError(err).Fatal("Cannot start the DNS over HTTPS proxy server")
} else {
<-shutdownC
}
@ -453,13 +448,14 @@ func startServer(c *cli.Context) {
close(dnsReadySignal)
}
if isAutoupdateEnabled(c) {
isRunningFromTerminal := isRunningFromTerminal()
if isAutoupdateEnabled(c, isRunningFromTerminal) {
// Wait for proxy-dns to come up (if used)
<-dnsReadySignal
if initUpdate() {
return
}
Log.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
logger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
go autoupdate(c.Duration("autoupdate-freq"), shutdownC)
}
@ -473,7 +469,7 @@ func startServer(c *cli.Context) {
hostname, err := validation.ValidateHostname(c.String("hostname"))
if err != nil {
Log.WithError(err).Fatal("Invalid hostname")
logger.WithError(err).Fatal("Invalid hostname")
}
clientID := c.String("id")
if !c.IsSet("id") {
@ -482,7 +478,7 @@ func startServer(c *cli.Context) {
tags, err := NewTagSliceFromCLI(c.StringSlice("tag"))
if err != nil {
Log.WithError(err).Fatal("Tag parse failure")
logger.WithError(err).Fatal("Tag parse failure")
}
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
@ -491,7 +487,7 @@ func startServer(c *cli.Context) {
listener, err := createListener("127.0.0.1:")
if err != nil {
listener.Close()
Log.WithError(err).Fatal("Cannot start Hello World Server")
logger.WithError(err).Fatal("Cannot start Hello World Server")
}
go func() {
startHelloWorldServer(listener, shutdownC)
@ -503,26 +499,26 @@ func startServer(c *cli.Context) {
url, err := validateUrl(c)
if err != nil {
Log.WithError(err).Fatal("Error validating url")
logger.WithError(err).Fatal("Error validating url")
}
Log.Infof("Proxying tunnel requests to %s", url)
logger.Infof("Proxying tunnel requests to %s", url)
// Fail if the user provided an old authentication method
if c.IsSet("api-key") || c.IsSet("api-email") || c.IsSet("api-ca-key") {
Log.Fatal("You don't need to give us your api-key anymore. Please use the new log in method. Just run cloudflared login")
logger.Fatal("You don't need to give us your api-key anymore. Please use the new log in method. Just run cloudflared login")
}
// Check that the user has acquired a certificate using the log in command
originCertPath, err := homedir.Expand(c.String("origincert"))
if err != nil {
Log.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
logger.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
}
ok, err := fileExists(originCertPath)
if err != nil {
Log.Fatalf("Cannot check if origin cert exists at path %s", c.String("origincert"))
logger.Fatalf("Cannot check if origin cert exists at path %s", c.String("origincert"))
}
if !ok {
Log.Fatalf(`Cannot find a valid certificate for your origin at the path:
logger.Fatalf(`Cannot find a valid certificate for your origin at the path:
%s
@ -535,7 +531,7 @@ If you don't have a certificate signed by Cloudflare, run the command:
// Easier to send the certificate as []byte via RPC than decoding it at this point
originCert, err := ioutil.ReadFile(originCertPath)
if err != nil {
Log.WithError(err).Fatalf("Cannot read %s to load origin certificate", originCertPath)
logger.WithError(err).Fatalf("Cannot read %s to load origin certificate", originCertPath)
}
tunnelMetrics := origin.NewTunnelMetrics()
@ -558,27 +554,29 @@ If you don't have a certificate signed by Cloudflare, run the command:
}
tunnelConfig := &origin.TunnelConfig{
EdgeAddrs: c.StringSlice("edge"),
OriginUrl: url,
Hostname: hostname,
OriginCert: originCert,
TlsConfig: tlsconfig.CreateTunnelConfig(c, c.StringSlice("edge")),
ClientTlsConfig: httpTransport.TLSClientConfig,
Retries: c.Uint("retries"),
HeartbeatInterval: c.Duration("heartbeat-interval"),
MaxHeartbeats: c.Uint64("heartbeat-count"),
ClientID: clientID,
ReportedVersion: Version,
LBPool: c.String("lb-pool"),
Tags: tags,
HAConnections: c.Int("ha-connections"),
HTTPTransport: httpTransport,
Metrics: tunnelMetrics,
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
ProtocolLogger: protoLogger,
Logger: Log,
IsAutoupdated: c.Bool("is-autoupdated"),
GracePeriod: c.Duration("grace-period"),
EdgeAddrs: c.StringSlice("edge"),
OriginUrl: url,
Hostname: hostname,
OriginCert: originCert,
TlsConfig: tlsconfig.CreateTunnelConfig(c, c.StringSlice("edge")),
ClientTlsConfig: httpTransport.TLSClientConfig,
Retries: c.Uint("retries"),
HeartbeatInterval: c.Duration("heartbeat-interval"),
MaxHeartbeats: c.Uint64("heartbeat-count"),
ClientID: clientID,
BuildInfo: buildInfo,
ReportedVersion: Version,
LBPool: c.String("lb-pool"),
Tags: tags,
HAConnections: c.Int("ha-connections"),
HTTPTransport: httpTransport,
Metrics: tunnelMetrics,
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
ProtocolLogger: protoLogger,
Logger: logger,
IsAutoupdated: c.Bool("is-autoupdated"),
GracePeriod: c.Duration("grace-period"),
RunFromTerminal: isRunningFromTerminal,
}
go writePidFile(connectedSignal, c.String("pidfile"))
@ -595,21 +593,21 @@ func runServer(c *cli.Context, wg *sync.WaitGroup, errC chan error, shutdownC ch
wg.Add(1)
metricsListener, err := listeners.Listen("tcp", c.String("metrics"))
if err != nil {
Log.WithError(err).Fatal("Error opening metrics server listener")
logger.WithError(err).Fatal("Error opening metrics server listener")
}
go func() {
errC <- metrics.ServeMetrics(metricsListener, shutdownC)
errC <- metrics.ServeMetrics(metricsListener, shutdownC, logger)
wg.Done()
}()
var errCode int
err = WaitForSignal(errC, shutdownC)
if err != nil {
Log.WithError(err).Fatal("Quitting due to error")
logger.WithError(err).Fatal("Quitting due to error")
raven.CaptureErrorAndWait(err, nil)
errCode = 1
} else {
Log.Info("Graceful shutdown...")
logger.Info("Graceful shutdown...")
}
// Wait for clean exit, discarding all errors
go func() {
@ -648,7 +646,7 @@ func initUpdate() bool {
if updateApplied() {
os.Args = append(os.Args, "--is-autoupdated=true")
if _, err := listeners.StartProcess(); err != nil {
Log.WithError(err).Error("Unable to restart server automatically")
logger.WithError(err).Error("Unable to restart server automatically")
return false
}
return true
@ -661,7 +659,7 @@ func autoupdate(freq time.Duration, shutdownC chan struct{}) {
if updateApplied() {
os.Args = append(os.Args, "--is-autoupdated=true")
if _, err := listeners.StartProcess(); err != nil {
Log.WithError(err).Error("Unable to restart server automatically")
logger.WithError(err).Error("Unable to restart server automatically")
}
close(shutdownC)
return
@ -673,11 +671,11 @@ func autoupdate(freq time.Duration, shutdownC chan struct{}) {
func updateApplied() bool {
releaseInfo := checkForUpdates()
if releaseInfo.Updated {
Log.Infof("Updated to version %s", releaseInfo.Version)
logger.Infof("Updated to version %s", releaseInfo.Version)
return true
}
if releaseInfo.Error != nil {
Log.WithError(releaseInfo.Error).Error("Update check failed")
logger.WithError(releaseInfo.Error).Error("Update check failed")
}
return false
}
@ -748,7 +746,7 @@ func writePidFile(waitForSignal chan struct{}, pidFile string) {
}
file, err := os.Create(pidFile)
if err != nil {
Log.WithError(err).Errorf("Unable to write pid to %s", pidFile)
logger.WithError(err).Errorf("Unable to write pid to %s", pidFile)
}
defer file.Close()
fmt.Fprintf(file, "%d", os.Getpid())
@ -790,16 +788,22 @@ func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
logrus.PanicLevel: filePath,
}
Log.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
logger.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
protoLogger.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
flags := make(map[string]interface{})
envs := make(map[string]string)
return nil
}
func logClientOptions(c *cli.Context) {
flags := make(map[string]interface{})
for _, flag := range c.LocalFlagNames() {
flags[flag] = c.Generic(flag)
}
if len(flags) > 0 {
logger.Infof("Flags %v", flags)
}
envs := make(map[string]string)
// Find env variables for Argo Tunnel
for _, env := range os.Environ() {
// All Argo Tunnel env variables start with TUNNEL_
@ -810,24 +814,33 @@ func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
}
}
}
Log.Infof("Argo Tunnel build and runtime configuration: %+v", BuildAndRuntimeInfo{
GoOS: runtime.GOOS,
GoVersion: runtime.Version(),
GoArch: runtime.GOARCH,
WarpVersion: Version,
WarpFlags: flags,
WarpEnvs: envs,
})
return nil
if len(envs) > 0 {
logger.Infof("Environmental variables %v", envs)
}
}
func isAutoupdateEnabled(c *cli.Context) bool {
if terminal.IsTerminal(int(os.Stdout.Fd())) {
Log.Info(noAutoupdateMessage)
func isAutoupdateEnabled(c *cli.Context, isRunningFromTerminal bool) bool {
if isRunningFromTerminal {
logger.Info(noAutoupdateMessage)
return false
}
return !c.Bool("no-autoupdate") && c.Duration("autoupdate-freq") != 0
}
func isRunningFromTerminal() bool {
return terminal.IsTerminal(int(os.Stdout.Fd()))
}
func userHomeDir() string {
// This returns the home dir of the executing user using OS-specific method
// for discovering the home dir. It's not recommended to call this function
// when the user has root permission as $HOME depends on what options the user
// use with sudo.
homeDir, err := homedir.Dir()
if err != nil {
logger.WithError(err).Fatal("Cannot determine home directory for the user.")
}
return homeDir
}

View File

@ -73,21 +73,21 @@ func runCommand(command string, args ...string) error {
cmd := exec.Command(command, args...)
stderr, err := cmd.StderrPipe()
if err != nil {
Log.WithError(err).Infof("error getting stderr pipe")
logger.WithError(err).Infof("error getting stderr pipe")
return fmt.Errorf("error getting stderr pipe: %v", err)
}
err = cmd.Start()
if err != nil {
Log.WithError(err).Infof("error starting %s", command)
logger.WithError(err).Infof("error starting %s", command)
return fmt.Errorf("error starting %s: %v", command, err)
}
commandErr, _ := ioutil.ReadAll(stderr)
if len(commandErr) > 0 {
Log.Errorf("%s: %s", command, commandErr)
logger.Errorf("%s: %s", command, commandErr)
}
err = cmd.Wait()
if err != nil {
Log.WithError(err).Infof("%s returned error", command)
logger.WithError(err).Infof("%s returned error", command)
return fmt.Errorf("%s returned with error: %v", command, err)
}
return nil
@ -159,7 +159,7 @@ func copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile strin
destConfigPath := filepath.Join(serviceConfigDir, defaultConfigFile)
destFile, exists, err := openFile(destConfigPath, true)
if err != nil {
Log.WithError(err).Infof("cannot open %s", destConfigPath)
logger.WithError(err).Infof("cannot open %s", destConfigPath)
return err
} else if exists {
// config already exists, do nothing
@ -185,7 +185,7 @@ func copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile strin
if err != nil {
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
}
Log.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
logger.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
}
return nil

View File

@ -41,7 +41,7 @@ func runApp(app *cli.App) {
isIntSess, err := svc.IsAnInteractiveSession()
if err != nil {
Log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
logger.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if isIntSess {
@ -51,7 +51,7 @@ func runApp(app *cli.App) {
elog, err := eventlog.Open(windowsServiceName)
if err != nil {
Log.WithError(err).Infof("Cannot open event log for %s", windowsServiceName)
logger.WithError(err).Infof("Cannot open event log for %s", windowsServiceName)
return
}
defer elog.Close()
@ -104,62 +104,62 @@ loop:
}
func installWindowsService(c *cli.Context) error {
Log.Infof("Installing Argo Tunnel Windows service")
logger.Infof("Installing Argo Tunnel Windows service")
exepath, err := os.Executable()
if err != nil {
Log.Infof("Cannot find path name that start the process")
logger.Infof("Cannot find path name that start the process")
return err
}
m, err := mgr.Connect()
if err != nil {
Log.WithError(err).Infof("Cannot establish a connection to the service control manager")
logger.WithError(err).Infof("Cannot establish a connection to the service control manager")
return err
}
defer m.Disconnect()
s, err := m.OpenService(windowsServiceName)
if err == nil {
s.Close()
Log.Errorf("service %s already exists", windowsServiceName)
logger.Errorf("service %s already exists", windowsServiceName)
return fmt.Errorf("service %s already exists", windowsServiceName)
}
config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription}
s, err = m.CreateService(windowsServiceName, exepath, config)
if err != nil {
Log.Infof("Cannot install service %s", windowsServiceName)
logger.Infof("Cannot install service %s", windowsServiceName)
return err
}
defer s.Close()
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
if err != nil {
s.Delete()
Log.WithError(err).Infof("Cannot install event logger")
logger.WithError(err).Infof("Cannot install event logger")
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
}
return nil
}
func uninstallWindowsService(c *cli.Context) error {
Log.Infof("Uninstalling Argo Tunnel Windows Service")
logger.Infof("Uninstalling Argo Tunnel Windows Service")
m, err := mgr.Connect()
if err != nil {
Log.Infof("Cannot establish a connection to the service control manager")
logger.Infof("Cannot establish a connection to the service control manager")
return err
}
defer m.Disconnect()
s, err := m.OpenService(windowsServiceName)
if err != nil {
Log.Infof("service %s is not installed", windowsServiceName)
logger.Infof("service %s is not installed", windowsServiceName)
return fmt.Errorf("service %s is not installed", windowsServiceName)
}
defer s.Close()
err = s.Delete()
if err != nil {
Log.Errorf("Cannot delete service %s", windowsServiceName)
logger.Errorf("Cannot delete service %s", windowsServiceName)
return err
}
err = eventlog.Remove(windowsServiceName)
if err != nil {
Log.Infof("Cannot remove event logger")
logger.Infof("Cannot remove event logger")
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
}
return nil

85
log/log.go Normal file
View File

@ -0,0 +1,85 @@
// this forks the logrus json formatter to rename msg -> message as that's the
// expected field. Ideally the logger should make it easier for us.
package log
import (
"encoding/json"
"fmt"
"time"
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
)
var (
DefaultTimestampFormat = time.RFC3339
)
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
}
func CreateLogger() *logrus.Logger {
logger := logrus.New()
logger.Out = colorable.NewColorableStderr()
logger.Formatter = &logrus.TextFormatter{ForceColors: true}
return logger
}
func (f *JSONFormatter) Format(entry *logrus.Entry) ([]byte, error) {
data := make(logrus.Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
prefixFieldClashes(data)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
data["time"] = entry.Time.Format(timestampFormat)
data["message"] = entry.Message
data["level"] = entry.Level.String()
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
// This is to not silently overwrite `time`, `msg` and `level` fields when
// dumping it. If this code wasn't there doing:
//
// logrus.WithField("level", 1).Info("hello")
//
// Would just silently drop the user provided level. Instead with this code
// it'll logged as:
//
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
//
// It's not exported because it's still using Data in an opinionated way. It's to
// avoid code duplication between the two default formatters.
func prefixFieldClashes(data logrus.Fields) {
if t, ok := data["time"]; ok {
data["fields.time"] = t
}
if m, ok := data["msg"]; ok {
data["fields.msg"] = m
}
if l, ok := data["level"]; ok {
data["fields.level"] = l
}
}

View File

@ -13,7 +13,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
const (
@ -21,7 +21,7 @@ const (
startupTime = time.Millisecond * 500
)
func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger *logrus.Logger) (err error) {
var wg sync.WaitGroup
// Metrics port is privileged, so no need for further access control
trace.AuthRequest = func(*http.Request) (bool, bool) { return true, true }
@ -39,7 +39,7 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
defer wg.Done()
err = server.Serve(l)
}()
log.WithField("addr", l.Addr()).Info("Starting metrics server")
logger.WithField("addr", l.Addr()).Info("Starting metrics server")
// server.Serve will hang if server.Shutdown is called before the server is
// fully started up. So add artificial delay.
time.Sleep(startupTime)
@ -51,10 +51,10 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
wg.Wait()
if err == http.ErrServerClosed {
log.Info("Metrics server stopped")
logger.Info("Metrics server stopped")
return nil
}
log.WithError(err).Error("Metrics server quit with error")
logger.WithError(err).Error("Metrics server quit with error")
return err
}

19
origin/build_info.go Normal file
View File

@ -0,0 +1,19 @@
package origin
import (
"runtime"
)
type BuildInfo struct {
GoOS string `json:"go_os"`
GoVersion string `json:"go_version"`
GoArch string `json:"go_arch"`
}
func GetBuildInfo() *BuildInfo {
return &BuildInfo{
GoOS: runtime.GOOS,
GoVersion: runtime.Version(),
GoArch: runtime.GOARCH,
}
}

View File

@ -27,7 +27,7 @@ type muxerMetrics struct {
outBoundRateMax *prometheus.GaugeVec
}
type tunnelMetrics struct {
type TunnelMetrics struct {
haConnections prometheus.Gauge
totalRequests prometheus.Counter
requestsPerTunnel *prometheus.CounterVec
@ -229,7 +229,7 @@ func convertRTTMilliSec(t time.Duration) float64 {
}
// Metrics that can be collected without asking the edge
func NewTunnelMetrics() *tunnelMetrics {
func NewTunnelMetrics() *TunnelMetrics {
haConnections := prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "ha_connections",
@ -305,7 +305,7 @@ func NewTunnelMetrics() *tunnelMetrics {
)
prometheus.MustRegister(serverLocations)
return &tunnelMetrics{
return &TunnelMetrics{
haConnections: haConnections,
totalRequests: totalRequests,
requestsPerTunnel: requestsPerTunnel,
@ -322,19 +322,19 @@ func NewTunnelMetrics() *tunnelMetrics {
}
}
func (t *tunnelMetrics) incrementHaConnections() {
func (t *TunnelMetrics) incrementHaConnections() {
t.haConnections.Inc()
}
func (t *tunnelMetrics) decrementHaConnections() {
func (t *TunnelMetrics) decrementHaConnections() {
t.haConnections.Dec()
}
func (t *tunnelMetrics) updateMuxerMetrics(connectionID string, metrics *h2mux.MuxerMetrics) {
func (t *TunnelMetrics) updateMuxerMetrics(connectionID string, metrics *h2mux.MuxerMetrics) {
t.muxerMetrics.update(connectionID, metrics)
}
func (t *tunnelMetrics) incrementRequests(connectionID string) {
func (t *TunnelMetrics) incrementRequests(connectionID string) {
t.concurrentRequestsLock.Lock()
var concurrentRequests uint64
var ok bool
@ -356,25 +356,25 @@ func (t *tunnelMetrics) incrementRequests(connectionID string) {
t.concurrentRequestsPerTunnel.WithLabelValues(connectionID).Inc()
}
func (t *tunnelMetrics) decrementConcurrentRequests(connectionID string) {
func (t *TunnelMetrics) decrementConcurrentRequests(connectionID string) {
t.concurrentRequestsLock.Lock()
if _, ok := t.concurrentRequests[connectionID]; ok {
t.concurrentRequests[connectionID] -= 1
} else {
Log.Error("Concurrent requests per tunnel metrics went wrong; you can't decrement concurrent requests count without increment it first.")
logger.Error("Concurrent requests per tunnel metrics went wrong; you can't decrement concurrent requests count without increment it first.")
}
t.concurrentRequestsLock.Unlock()
t.concurrentRequestsPerTunnel.WithLabelValues(connectionID).Dec()
}
func (t *tunnelMetrics) incrementResponses(connectionID, code string) {
func (t *TunnelMetrics) incrementResponses(connectionID, code string) {
t.responseByCode.WithLabelValues(code).Inc()
t.responseCodePerTunnel.WithLabelValues(connectionID, code).Inc()
}
func (t *tunnelMetrics) registerServerLocation(connectionID, loc string) {
func (t *TunnelMetrics) registerServerLocation(connectionID, loc string) {
t.locationLock.Lock()
defer t.locationLock.Unlock()
if oldLoc, ok := t.oldServerLocations[connectionID]; ok && oldLoc == loc {

View File

@ -73,7 +73,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
case tunnelError := <-s.tunnelErrors:
tunnelsActive--
if tunnelError.err != nil {
Log.WithError(tunnelError.err).Warn("Tunnel disconnected due to error")
logger.WithError(tunnelError.err).Warn("Tunnel disconnected due to error")
tunnelsWaiting = append(tunnelsWaiting, tunnelError.index)
s.waitForNextTunnel(tunnelError.index)
@ -109,10 +109,10 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
s.lastResolve = time.Now()
s.resolverC = nil
if result.err == nil {
Log.Debug("Service discovery refresh complete")
logger.Debug("Service discovery refresh complete")
s.edgeIPs = result.edgeIPs
} else {
Log.WithError(result.err).Error("Service discovery error")
logger.WithError(result.err).Error("Service discovery error")
}
}
}
@ -121,12 +121,12 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
func (s *Supervisor) initialize(ctx context.Context, connectedSignal chan struct{}) error {
edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs)
if err != nil {
Log.Infof("ResolveEdgeIPs err")
logger.Infof("ResolveEdgeIPs err")
return err
}
s.edgeIPs = edgeIPs
if s.config.HAConnections > len(edgeIPs) {
Log.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, len(edgeIPs))
logger.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, len(edgeIPs))
s.config.HAConnections = len(edgeIPs)
}
s.lastResolve = time.Now()

View File

@ -7,7 +7,6 @@ import (
"net"
"net/http"
"net/url"
"runtime"
"strconv"
"strings"
"sync"
@ -28,7 +27,7 @@ import (
rpc "zombiezen.com/go/capnproto2/rpc"
)
var Log *logrus.Logger
var logger *logrus.Logger
const (
dialTimeout = 15 * time.Second
@ -48,17 +47,19 @@ type TunnelConfig struct {
HeartbeatInterval time.Duration
MaxHeartbeats uint64
ClientID string
BuildInfo *BuildInfo
ReportedVersion string
LBPool string
Tags []tunnelpogs.Tag
HAConnections int
HTTPTransport http.RoundTripper
Metrics *tunnelMetrics
Metrics *TunnelMetrics
MetricsUpdateFreq time.Duration
ProtocolLogger *logrus.Logger
Logger *logrus.Logger
IsAutoupdated bool
GracePeriod time.Duration
RunFromTerminal bool
}
type dialError struct {
@ -92,18 +93,19 @@ func (c *TunnelConfig) RegistrationOptions(connectionID uint8, OriginLocalIP str
return &tunnelpogs.RegistrationOptions{
ClientID: c.ClientID,
Version: c.ReportedVersion,
OS: fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH),
OS: fmt.Sprintf("%s_%s", c.BuildInfo.GoOS, c.BuildInfo.GoArch),
ExistingTunnelPolicy: policy,
PoolName: c.LBPool,
Tags: c.Tags,
ConnectionID: connectionID,
OriginLocalIP: OriginLocalIP,
IsAutoupdated: c.IsAutoupdated,
RunFromTerminal: c.RunFromTerminal,
}
}
func StartTunnelDaemon(config *TunnelConfig, shutdownC <-chan struct{}, connectedSignal chan struct{}) error {
Log = config.Logger
logger = config.Logger
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-shutdownC
@ -138,7 +140,7 @@ func ServeTunnelLoop(ctx context.Context, config *TunnelConfig, addr *net.TCPAdd
err, recoverable := ServeTunnel(ctx, config, addr, connectionID, connectedFuse, &backoff)
if recoverable {
if duration, ok := backoff.GetBackoffDuration(ctx); ok {
Log.Infof("Retrying in %s seconds", duration)
logger.Infof("Retrying in %s seconds", duration)
backoff.Backoff(ctx)
continue
}
@ -171,7 +173,7 @@ func ServeTunnel(
// Returns error from parsing the origin URL or handshake errors
handler, originLocalIP, err := NewTunnelHandler(ctx, config, addr.String(), connectionID)
if err != nil {
errLog := Log.WithError(err)
errLog := logger.WithError(err)
switch err.(type) {
case dialError:
errLog.Error("Unable to dial edge")
@ -218,21 +220,21 @@ func ServeTunnel(
registerErr := <-registerErrC
wg.Wait()
if err != nil {
Log.WithError(err).Error("Tunnel error")
logger.WithError(err).Error("Tunnel error")
return err, true
}
if registerErr != nil {
// Don't retry on errors like entitlement failure or version too old
if e, ok := registerErr.(printableRegisterTunnelError); ok {
Log.Error(e)
logger.Error(e)
return e.cause, !e.permanent
} else if e, ok := registerErr.(dupConnRegisterTunnelError); ok {
Log.Info("Already connected to this server, selecting a different one")
logger.Info("Already connected to this server, selecting a different one")
return e, true
}
// Only log errors to Sentry that may have been caused by the client side, to reduce dupes
raven.CaptureError(registerErr, nil)
Log.Error("Cannot register")
logger.Error("Cannot register")
return err, true
}
return nil, false
@ -249,7 +251,7 @@ func IsRPCStreamResponse(headers []h2mux.Header) bool {
}
func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfig, connectionID uint8, originLocalIP string) error {
logger := Log.WithField("subsystem", "rpc")
logger := logger.WithField("subsystem", "rpc")
logger.Debug("initiating RPC stream to register")
stream, err := muxer.OpenStream([]h2mux.Header{
{Name: ":method", Value: "RPC"},
@ -304,7 +306,7 @@ func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfi
}
func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration) error {
logger := Log.WithField("subsystem", "rpc")
logger := logger.WithField("subsystem", "rpc")
logger.Debug("initiating RPC stream to unregister")
stream, err := muxer.OpenStream([]h2mux.Header{
{Name: ":method", Value: "RPC"},
@ -335,7 +337,7 @@ func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration) error {
func LogServerInfo(logger *logrus.Entry,
promise tunnelrpc.ServerInfo_Promise,
connectionID uint8,
metrics *tunnelMetrics,
metrics *TunnelMetrics,
) {
serverInfoMessage, err := promise.Struct()
if err != nil {
@ -347,7 +349,7 @@ func LogServerInfo(logger *logrus.Entry,
logger.WithError(err).Warn("Failed to retrieve server information")
return
}
Log.Infof("Connected to %s", serverInfo.LocationName)
logger.Infof("Connected to %s", serverInfo.LocationName)
metrics.registerServerLocation(uint8ToString(connectionID), serverInfo.LocationName)
}
@ -398,7 +400,7 @@ type TunnelHandler struct {
httpClient http.RoundTripper
tlsConfig *tls.Config
tags []tunnelpogs.Tag
metrics *tunnelMetrics
metrics *TunnelMetrics
// connectionID is only used by metrics, and prometheus requires labels to be string
connectionID string
}
@ -407,12 +409,12 @@ var dialer = net.Dialer{DualStack: true}
// NewTunnelHandler returns a TunnelHandler, origin LAN IP and error
func NewTunnelHandler(ctx context.Context, config *TunnelConfig, addr string, connectionID uint8) (*TunnelHandler, string, error) {
url, err := validation.ValidateUrl(config.OriginUrl)
originURL, err := validation.ValidateUrl(config.OriginUrl)
if err != nil {
return nil, "", fmt.Errorf("Unable to parse origin url %#v", url)
return nil, "", fmt.Errorf("Unable to parse origin url %#v", originURL)
}
h := &TunnelHandler{
originUrl: url,
originUrl: originURL,
httpClient: config.HTTPTransport,
tlsConfig: config.ClientTlsConfig,
tags: config.Tags,
@ -464,11 +466,11 @@ func (h *TunnelHandler) ServeStream(stream *h2mux.MuxedStream) error {
h.metrics.incrementRequests(h.connectionID)
req, err := http.NewRequest("GET", h.originUrl, h2mux.MuxedStreamReader{MuxedStream: stream})
if err != nil {
Log.WithError(err).Panic("Unexpected error from http.NewRequest")
logger.WithError(err).Panic("Unexpected error from http.NewRequest")
}
err = H2RequestHeadersToH1Request(stream.Headers, req)
if err != nil {
Log.WithError(err).Error("invalid request received")
logger.WithError(err).Error("invalid request received")
}
h.AppendTagHeaders(req)
cfRay := FindCfRayHeader(req)
@ -501,7 +503,7 @@ func (h *TunnelHandler) ServeStream(stream *h2mux.MuxedStream) error {
}
func (h *TunnelHandler) logError(stream *h2mux.MuxedStream, err error) {
Log.WithError(err).Error("HTTP request error")
logger.WithError(err).Error("HTTP request error")
stream.WriteHeaders([]h2mux.Header{{Name: ":status", Value: "502"}})
stream.Write([]byte("502 Bad Gateway"))
h.metrics.incrementResponses(h.connectionID, "502")
@ -509,20 +511,20 @@ func (h *TunnelHandler) logError(stream *h2mux.MuxedStream, err error) {
func (h *TunnelHandler) logRequest(req *http.Request, cfRay string) {
if cfRay != "" {
Log.WithField("CF-RAY", cfRay).Infof("%s %s %s", req.Method, req.URL, req.Proto)
logger.WithField("CF-RAY", cfRay).Infof("%s %s %s", req.Method, req.URL, req.Proto)
} else {
Log.Warnf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", req.Method, req.URL, req.Proto)
logger.Warnf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", req.Method, req.URL, req.Proto)
}
Log.Debugf("Request Headers %+v", req.Header)
logger.Debugf("Request Headers %+v", req.Header)
}
func (h *TunnelHandler) logResponse(r *http.Response, cfRay string) {
if cfRay != "" {
Log.WithField("CF-RAY", cfRay).Infof("%s", r.Status)
logger.WithField("CF-RAY", cfRay).Infof("%s", r.Status)
} else {
Log.Infof("%s", r.Status)
logger.Infof("%s", r.Status)
}
Log.Debugf("Response Headers %+v", r.Header)
logger.Debugf("Response Headers %+v", r.Header)
}
func (h *TunnelHandler) UpdateMetrics(connectionID string) {

View File

@ -8,10 +8,12 @@ import (
"io/ioutil"
"net"
log "github.com/sirupsen/logrus"
cli "gopkg.in/urfave/cli.v2"
"github.com/cloudflare/cloudflared/log"
"gopkg.in/urfave/cli.v2"
)
var logger = log.CreateLogger()
// CLIFlags names the flags used to configure TLS for a command or subsystem.
// The nil value for a field means the flag is ignored.
type CLIFlags struct {
@ -29,7 +31,7 @@ func (f CLIFlags) GetConfig(c *cli.Context) *tls.Config {
if c.IsSet(f.Cert) && c.IsSet(f.Key) {
cert, err := tls.LoadX509KeyPair(c.String(f.Cert), c.String(f.Key))
if err != nil {
log.WithError(err).Fatal("Error parsing X509 key pair")
logger.WithError(err).Fatal("Error parsing X509 key pair")
}
config.Certificates = []tls.Certificate{cert}
config.BuildNameToCertificate()
@ -53,11 +55,11 @@ func (f CLIFlags) GetConfig(c *cli.Context) *tls.Config {
func LoadCert(certPath string) *x509.CertPool {
caCert, err := ioutil.ReadFile(certPath)
if err != nil {
log.WithError(err).Fatalf("Error reading certificate %s", certPath)
logger.WithError(err).Fatalf("Error reading certificate %s", certPath)
}
ca := x509.NewCertPool()
if !ca.AppendCertsFromPEM(caCert) {
log.WithError(err).Fatalf("Error parsing certificate %s", certPath)
logger.WithError(err).Fatalf("Error parsing certificate %s", certPath)
}
return ca
}
@ -66,23 +68,23 @@ func LoadOriginCertsPool() *x509.CertPool {
// First, obtain the system certificate pool
certPool, systemCertPoolErr := x509.SystemCertPool()
if systemCertPoolErr != nil {
log.Warn("error obtaining the system certificates: %s", systemCertPoolErr)
logger.Warnf("error obtaining the system certificates: %s", systemCertPoolErr)
certPool = x509.NewCertPool()
}
// Next, append the Cloudflare CA pool into the system pool
if !certPool.AppendCertsFromPEM([]byte(cloudflareRootCA)) {
log.Warn("could not append the CF certificate to the system certificate pool")
logger.Warn("could not append the CF certificate to the system certificate pool")
if systemCertPoolErr != nil { // Obtaining both certificates failed; this is a fatal error
log.WithError(systemCertPoolErr).Fatalf("Error loading the certificate pool")
logger.WithError(systemCertPoolErr).Fatalf("Error loading the certificate pool")
}
}
// Finally, add the Hello certificate into the pool (since it's self-signed)
helloCertificate, err := GetHelloCertificateX509()
if err != nil {
log.Warn("error obtaining the Hello server certificate")
logger.Warn("error obtaining the Hello server certificate")
}
certPool.AddCert(helloCertificate)

View File

@ -8,16 +8,18 @@ import (
"sync"
"syscall"
"gopkg.in/urfave/cli.v2"
"github.com/cloudflare/cloudflared/log"
"github.com/cloudflare/cloudflared/metrics"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/cache"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"gopkg.in/urfave/cli.v2"
)
var logger = log.CreateLogger()
// Listener is an adapter between CoreDNS server and Warp runnable
type Listener struct {
server *dnsserver.Server
@ -28,14 +30,14 @@ type Listener struct {
func Run(c *cli.Context) error {
metricsListener, err := net.Listen("tcp", c.String("metrics"))
if err != nil {
log.WithError(err).Fatal("Failed to open the metrics listener")
logger.WithError(err).Fatal("Failed to open the metrics listener")
}
go metrics.ServeMetrics(metricsListener, nil)
go metrics.ServeMetrics(metricsListener, nil, logger)
listener, err := CreateListener(c.String("address"), uint16(c.Uint("port")), c.StringSlice("upstream"))
if err != nil {
log.WithError(err).Errorf("Failed to create the listeners")
logger.WithError(err).Errorf("Failed to create the listeners")
return err
}
@ -43,7 +45,7 @@ func Run(c *cli.Context) error {
readySignal := make(chan struct{})
err = listener.Start(readySignal)
if err != nil {
log.WithError(err).Errorf("Failed to start the listeners")
logger.WithError(err).Errorf("Failed to start the listeners")
return listener.Stop()
}
<-readySignal
@ -57,7 +59,7 @@ func Run(c *cli.Context) error {
// Shut down server
err = listener.Stop()
if err != nil {
log.WithError(err).Errorf("failed to stop")
logger.WithError(err).Errorf("failed to stop")
}
return err
}
@ -78,7 +80,7 @@ func createConfig(address string, port uint16, p plugin.Handler) *dnsserver.Conf
// Start blocks for serving requests
func (l *Listener) Start(readySignal chan struct{}) error {
defer close(readySignal)
log.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server")
logger.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server")
// Start UDP listener
if udp, err := l.server.ListenPacket(); err == nil {
@ -119,7 +121,7 @@ func CreateListener(address string, port uint16, upstreams []string) (*Listener,
// Build the list of upstreams
upstreamList := make([]Upstream, 0)
for _, url := range upstreams {
log.WithField("url", url).Infof("Adding DNS upstream")
logger.WithField("url", url).Infof("Adding DNS upstream")
upstream, err := NewUpstreamHTTPS(url)
if err != nil {
return nil, errors.Wrap(err, "failed to create HTTPS upstream")

View File

@ -52,7 +52,7 @@ type RegistrationOptions struct {
ConnectionID uint8 `capnp:"connectionId"`
OriginLocalIP string `capnp:"originLocalIp"`
IsAutoupdated bool `capnp:"isAutoupdated"`
gracePeriodNanoSec int64 `capnp:"gracePeriodNanoSec"`
RunFromTerminal bool `capnp:"runFromTerminal"`
}
func MarshalRegistrationOptions(s tunnelrpc.RegistrationOptions, p *RegistrationOptions) error {

View File

@ -37,6 +37,8 @@ struct RegistrationOptions {
originLocalIp @7 :Text;
# whether Argo Tunnel client has been autoupdated
isAutoupdated @8 :Bool;
# whether Argo Tunnel client is run from a terminal
runFromTerminal @9 :Bool;
}
struct Tag {

View File

@ -403,6 +403,14 @@ func (s RegistrationOptions) SetIsAutoupdated(v bool) {
s.Struct.SetBit(24, v)
}
func (s RegistrationOptions) RunFromTerminal() bool {
return s.Struct.Bit(25)
}
func (s RegistrationOptions) SetRunFromTerminal(v bool) {
s.Struct.SetBit(25, v)
}
// RegistrationOptions_List is a list of RegistrationOptions.
type RegistrationOptions_List struct{ capnp.List }
@ -1218,86 +1226,88 @@ func (p TunnelServer_unregisterTunnel_Results_Promise) Struct() (TunnelServer_un
return TunnelServer_unregisterTunnel_Results{s}, err
}
const schema_db8274f9144abc7e = "x\xda\x9cU_\x88Te\x1b\x7f~\xef;3g\x84" +
"]g\x0fg\x04\xbf\xe1\x93\xe5\x93\x15\xff\xe0\xfa\xe9\xa7" +
"\xfb\xa1\xdb\x9f\xd9\xdd\xd4\x98m]\xe7\xdd\xd1\x10\xf5\xc2" +
"\xe3\xcc\xeb\xec\xd9f\xce\x19\xce9c)\xa4%B " +
"$\x96uc\x14\xe4e\x17\x15t\x11\x84\x81\xdd$\xe1" +
"\x85\x08\x19EB\x94l\xa1(\xd6\xa2\x90\x91\x9cx\xcf" +
"\xec\x999\xae\xa5\xd6\xdd9\xcf\xfb>\xcf\xf3{\xfe\xfc" +
"~\xef\xea\x1f\xd9\x10[\x93\xfc.I$6&S\xc1" +
"\xe3\xf5\x0b\xa7\xff\xff\xe6\xf9\xa3\xa4\xe7Xp\xe8\xcch" +
"\xf6\x8e\x7f\xe4[\"\xac\xbd\xc4\x0e\xc2\xb8\xca4\"c" +
"\x9am%\x04\x17V\x9e\xf9\xe4\xc4G\xaf\xbcEb)" +
"@\x94\xd0\x88\xd6\xdee\xbf\x81`\xe8<O\x08\xde\xf8" +
"\xfa\xd3\xf1\xfak\xa7N\x93\xbe4:\xdf\xc0\x19\xa3D" +
"\xb0\xa0\x80\xcbg\xd7$>n\x9d$\xb9:\xea\xe7\xd7" +
"\x94\xeb0\xff\x80\x10,\xba1\xd2m\xdf<r\x96\xf4" +
"\x1c:(Z\x17\xbf\xe7\xa30n\xabO\xe3\x97\xf0\xf2" +
"\xe8\xae\x93\xaf'\xa7O\x9e#\x91C\xfcvJ\xdd~" +
"5\xe1\xc2x7L\xfev\"\x00!\xc8}\xf8\xd8\xfb" +
"#\x95o\xce\xcf\x89\x1dV\xf6Ej\xc6\xb8\xa4\xfc\x8c" +
"\x8b\xa9\xe7\x09\x01\x9b6\xff\xf5\xd2WO^\x8e\x95\xd0" +
"\xaf\xfd\x00J\x04\xe3\xcf\xee\x9a\x9a\xf7\xe2\x95+\xb3%" +
"@\x1d\xfdG\x0bK\x18\xd0T\xf5\x03{\x86\xe5\xee\xf5" +
";\xae\x91\x9e\xe3\xf74r\xbb6\x08Cj*\x89\xa9" +
"\x9d3\xae\xaa\xaf\xe0\xf8\xa1\x8d[7,\xfel&\x1e" +
"\xee\xa26\xa3\xc2M\x87\xe1\xf6\xad\xbf\xfe\xf4\x92\xe3\x9f" +
"\xcf\xccA\x1d^L\xa6W\xc0X\x90V\x11\xf5t\x9e" +
"ps\xf3;_\xe62\xb9[s\xfa\x11vo =" +
"\x05\xa3\x10\xde\xdd\x94\xfe\x89\xfa\x03\xbfi\xdb\xb2\xe66" +
"\x12\xe5\xffF\x9f\xe5Ue\xb3a7\x067\xbd`y" +
"\xbeeW\xb7\x85\xf6|\xd1\xa9Y\xe5\x03E@t\x81" +
"\x11\xe9\x8b\x06\x89\x00}\xc1N\"0]\x1f!\xca[" +
"U\xdbqeP\xb1\xbc\xb2c\xdb\x92x\xd9?\xbc\xd7" +
"\xac\x99vY\xb6\x13\xa5\xeeO\xd4JP\x92\xee~\xe9" +
"\xaej\xda\xae\xacZ\x9e/\xdd\x96\xb9/_4]\xb3" +
"\xee\x89\x04O\x10%@\xa4w\x9f\"\x12=\x1c\xe2\xdf" +
"\x0cA\xd55\xcb\xb2(]XNe\xdc\xb4\x9d\x12\x97" +
"e$\x89!Ih'\x9d\xffw\x93NH\xafY\xf3" +
"=j{=\xd8\x7f\x8ew\xd1\xcc\x84\x90\xbb\xda\x907" +
"\xedT\x04\xe3\x10E\x06\x1d\xc8*\xca\xe8[F\x89\xc4" +
"\x18\x87\xd8\xc1\xa03\x96\x0d\xdb\xba}\x84H\x149\xc4" +
"n\x86\xc0q\xad\xaae?%\x89\xbb>\xba\x89\xa1\x9b" +
"\x10L:\x9eo\x9buID\xe8\"\x86.\xc2a\xa7" +
"\xe1[\x8e\xed\xa1\xa7C\x07\x02zb-\xf8\x93\x01\x0f" +
"7\xfdIi\xfbV\xd9T\xceD\xe1l;\x90\x17\x13" +
"\x89!\x0e1\x16\x83\\\xf8_\xac\x8e\x08\xf2\x96\xbd\x9d" +
":\xb4\xe7\xe4\x81\x08U\xaf\xac\x9bV-\xfa\x8b\x8a\x19" +
"&\xed\x99\xce\x9d\x07\xe1\x9b\x08\xbb\xea\x86\xe8\xb66z" +
"\xc3\x0a\x15\xc6\xbe6\xc6\x1b\xaa\x83\xd79\xc4\xaf1\x8c" +
"\xb7U\x07\x7f\xe6\x10\xbf\xc70\xde\xc9\x11\x89[\x1c\x13" +
"`\x00\xcf\x82\x13\xe9w\xdf#\x9a\x00G\xa9\x0b\x0cz" +
"\x82g\x91 2\xe6a\x94\xa8\x94V\xf6\xac\xb2'\x13" +
"Y$\x15\xb7\xb0\x82\xa8\xd4\xa5\xec\xcb\x94=\xc5\xb2H" +
"\x11\x19K0ET\xeaS\xf6\xd5\xca\xae%\xb3\x8a\x96" +
"F?\\\xa2\xd2Je_\xaf\xec\xe9\x85Y\xa4\x89\x8c" +
"\x81\xd0\xbeN\xd9\x87\xc0\x10\x94k\x96\xb4\xfdB%>" +
"\xce\xfd\xd2\xf5,\xc7\x8e\xfe\xb9\xe3\xb5\xfb%gY\x89" +
"\xd6\xae\x15\x9d\x8c\xa2%2\x1d\xe9& C\x08\x1a\x8e" +
"S\x1b\xbfwM2\xbeY\xf50\x9fP\xe4@OG" +
"\x0a\x09\xca\x18\x84\xa4-\xfb\x16e\x1c\xbbPA\x8a\x18" +
"R\xed\xb9\x8d9\xd4[6k\x85F\x1b\x89\xe5\x0d7" +
"}\xa7\xd9\xa0\xde\x8a\xe9\xcb\x0a@\x0c\x88M\x94\xcd\x9d" +
"hocp\x9bYU\x13L\xb7'\xb8|\x05\x91\xe8" +
"\xe3\x10\xabc\x13\xecW[\xb6\x8cC\xacc\xc8\xa8U" +
"oo\xd4~\xb3\xd6\x94\xf7\xed\xce\xc34\xa5*\xfd\xd6" +
"W\xc1\xde\xe7\xf4\x15MW3\xeb\xde?\xf4\x9e\x90^" +
"FIC\\\x8f\x06\x89D\x9aCd\x19\xf2n\xa8\x1c" +
"\xe8\xe9H\xf6\x1c\"\xf2\xbfJ\x97oei\xb10I" +
"\xd4~'\x11=\x0f\xba8HL/h\xe8<M\x88" +
"^\"\xfd\x09\x97\x98>\xa0\x81\xb5\x9ffDO\xb0\xbe" +
"\xfc\x181}\x89\x16D:E\xf9V\xca!\x04Qu" +
"\xd4\x1b\xd67\x84 \x12CDzF4\x84\"\x1e\xbd" +
"\xdd\xf7ii\xaf\xf7(\x1d\x8b\xde\xad\x87\xf7\xab\x95'" +
"\xa3\xf0\xaan\xc5\xe2N\x11\x89.\x0e\xb1\x90!\xa89" +
"\xb3\xc2\x96\x19\x8f\xad\xd0\x83\x04\xa7\x058\x92\x9d\x8cr" +
"V\xf1{\xda\xf1M\xa5\x89\xbb9\xc4dl[\xa52" +
"\xee\xe1\x10\xb5\x98\xdeXJ\x99&9\xc4\xd1\x8e\xde\xbc" +
"|\x8cH\x1c\xe5\x10'\x184\xe9\xba\x11$\xad\xe9v" +
"T\xb2\xe6T\xc7,[z\x8a\xb6\xb3LUG\x8a\x9f" +
"\x0d\xe9\xd6M[\xda\xf07\x9bV\xad\xe9J\xb5Z-" +
"\xd2\xfd\x11\x00\x00\xff\xffy%\x9bh"
const schema_db8274f9144abc7e = "x\xda\x9cU_l\x14U\x17\xff\x9d{w;\xdb\xa4" +
"e;\x99m\x02\x1bH\x13\xd2\x86B\xbe\xf2\xc1\xc7\x87" +
"B\xfd\xd3?\x02\xba\xb5\x94\xbdl1\x08}`\xd8\xbd" +
"l\xa7\xce\xcelffQH\x04%\x18#\x89\x04E" +
"^0\x9a@\xe2\x8b\x0fj\xe2\x83\x89\xc1\x04_\xe4\x81" +
"\x07^\xd4h$1J\x88\x91h\x88\x8d\x9ahb\xc6" +
"\xdc\xd9\xce\xecR\x14\xd0\xb7\x99\xdf=\xf7\x9c\xdf9\xf7" +
"\x9c\xdfY\xf7+\x1be\xeb\xd3_\xa7\x01\xb1%\xdd\x11" +
">X\xbbr\xfe\xbe3\x97\x8fC\xcf\xb3\xf0\xc8\x85\x89" +
"\xdco\xc1\xb1\xaf\x00\xda\xf0\x19;L\xc6\xf7L\x03\x8c" +
"\xebl\x07(\xbc\xf2\x9f\x0b\x1f\x9ez\xff\xc5\xd7!V" +
"\x11\x01)\x0d\xd8\xf0\x07\xfb\x9d@\x86\xceG@\xe1k" +
"_|4U{\xe5\xecy\xe8\xab\xe2\xf3\xcd\x9c1\xa4" +
"\xc2\xde\x02]\xbd\xb8>\xf5A\xf3$\xcd\xd5\xd1\x10\xbf" +
"\xa1\xae\x8e\xf1wA\xe1\x8a\x1f\xc7\xbb\x9d\x9b\xc7.B" +
"\xcfS\x8bE\xd3\xf0\x1b>A\xc6/\xea\xd3\xf8)2" +
"\x9e\xd8{\xfa\xd5\xf4\xf5\xd3\x97 \xf2\xd4n\xdd\xa1\xac" +
"_Nyd\x9c\x8b\x82\xbf\x91\xba\x9f\x81\xc2\xfc{\x0f" +
"\xbc3^\xf9\xf2\xf2\"\xdfQf\x9d\xda\xbc\xd1\xab\xa9" +
"/]{\x1a\x14\xb2\xeb\xe6\xb2\xe7>\x7f\xf8j[\x0a" +
"5\xed[B*\x9czb\xef\\\xe7\xb3\xd7\xae-\xa4" +
"@\xea\xc8\xd4\xa2\x14\x1a\x9a\xca~\xe3\xbe19\xb3i" +
"\xf7\x0d\xe8y~K!\xcfh\xc3d\xbc\x15\x059\xa7" +
"]2Vg4 <yd\xcb\x8e\xcd+?\x9eo" +
"w\xa7g\xe6\x95\xbb\x81\x8crw`\xd3\x0f\x8f\x0e\x9c" +
"\xfcd~\x11\xeb\xc8pkf\x0d\x19\xbb\x94\x1fC(" +
"\xe3\x9b\xdb\xde\xfc4\x9f\xcd\xff\xbc\xa8\x1eQ\xf5\x1a\x99" +
"92^\x8al_\xc8|\x87\xa10h8\x8e\xb4\xbd" +
"z\xaa\xfc\xdf\xf8\xb3\xbc\xb6l\xd6\x9d\xfa\xf0\xd6g," +
"?\xb0\x9c\xeat\x84\x8f\x14]\xdb*\x1f*\x12\x89." +
"b\x80\xbeb\x18 \xd2{\xf7\x00\xc4t}\x1c\x18\xb1" +
"\xaa\x8e\xeb\xc9\xb0b\xf9e\xd7q$x98\xba\xdf" +
"\xb4M\xa7,\x93@\x1d\xb7\x07j\x06(I\xef\xa0\xf4" +
"\xd66\x1cOV-?\x90^\x13\xee\x1f)\x9a\x9eY" +
"\xf3E\x8a\xa7\x80\x14\x01z\xf7Y@\xf4p\x12\xcb\x19" +
"\x85U\xcf,\xcb\xa2\xf4\xc8r+S\xa6\xe3\x96\xb8," +
"S\x1a\x8c\xd2\xa0$\xe8\x92\x7f\x1at\xa7\xf4\x1bv\xe0" +
"#\xb9u\xe7\xfb\x8bn\x17\xcdlD\xb9+\xa1\xbcu" +
"\x8f\x1a0N\xa2\xc8H'\xca\xa9\x91\xd1\xb7O\x00b" +
"\x92\x93\xd8\xcdHg,\x17\x95u\xd78 \x8a\x9c\xc4" +
"\x0c\xa3\xd0\xf5\xac\xaa\xe5<\"\xc1\xbd\x80\xba\xc1\xa8\x1b" +
"\x14\xce\xba~\xe0\x985\x09\x80\xba\xc0\xa8\x0bt\xd4\xad" +
"\x07\x96\xeb\xf8\xd4\xd3\x1a\x07\x10\xf5\xb4\x95\xe0/\x1ex" +
"\xac\x11\xccJ'\xb0\xca\xa6\xba\x0cDo\xdb\xa2\xbc\x12" +
"\x10\xa3\x9c\xc4d\x1b\xe5\xc2\xff\xda\xf2\x88)o\xdf\xdf" +
"\xcaC{J\x1e\x8aY\xf5\xc9\x9ai\xd9\xf1_\x9c\xcc" +
"\x18\xb4\xc7[6w\xe2\xb73\xaa\xaa\x17\xb1\xdbQ\xef" +
"\x8b2T\x1c\x07c\x8eF'M\x00\xa5\x0cq*\xe5" +
"\xa8E\xd3\xd0i\x1c(u)|)\xb5\x98\x1a\xbd\x94" +
"\x07J=\x0a_N\x8c\x88\xe7\x88\x03\xc62z\x1b(" +
"-W\xf0\xa02O\xf1\x1c\xa5\x00c r\xdf\xaf\xf0" +
"u\x0aO\xa7r\x94\x06\x8c!Z\x03\x94\x06\x15\xbeE" +
"\xe1\x1d,G\x1d\x801Fs@iT\xe1\x93\x0a\xd7" +
"\xd295\xa1F\x81<\xa0\xf4\x98\xc2\xa7\x15\x9eY\x9a" +
"\xa3\x8c\x1a\xd7\x08/*|F\xe1\x9d\xcbr\xd4\x09\x18" +
"O\xd21\xa0\xb4[\xe1\x15b\x14\x96mK:A\xa1" +
"\xd2\xfe\xe2\x07\xa5\xe7[\xae\x13\xffs\xd7OJ*\x17" +
"\x06\x97\x9a\xedXt\xb3jr)\xdbRw\x10eA" +
"a\xddu\xed\xa9[;)\x1b\x98U\x9f\x96\x80\x8a\x9c" +
"\xa8\xa7\xa5\x96 \x05\x86\xd1\\\x97\x03\x0bY\xd7)T" +
"\xa8\x03\x8c:\x92\xa7\x9dt\xd1W6\xedB=ab" +
"\xf9c\x8d\xc0m\xd4\xd1W1\x03Y!\x02#\x02\x85" +
"^\xc3\xd9\xe6\xb9\xb5i\x92^\xcdrL\x1b\xc9I\xdc" +
"\x03lq;\xf4\xd5\x87\xa7\xcd\xaaz\xfeL\xd2\xa2\xab" +
"\xd7\x00\xa2\x9f\x93X\xd7\xd6\xa2C\xaaE\x079\x89\xff" +
"3\xca\xaa9I\xda\xf1\xa0i7\xe4m\x8dw7A" +
"\xaa\xca\xa0\xf9Up\x0e\xb8\xfdE\xd3\xd3\xcc\x9a\xff/" +
"o\xef\x94~V\xe9J\xbb\x98\x0d\x03\"\xc3I\xe4\x18" +
"\x8dx\x91\xecPOK\xef\x17M1\xff\xbbp#\xcd" +
"(\xcd\x11N\x03\xc9\x92\xa5x\xb7\xe8\xe20\x98^\xd0" +
"\xa8\xb5\xd7(^c\xfaC\x1e\x98\xbeQ#\x96\xecu" +
"\x8a\xf7\xb7\xbe\xfa\x04\x98>\xa0\x85\xb1\xc8a\xa4\x19r" +
"\x94\xc28;\xf4E\xf9\x8dR\x18+)\xc5b\x08\x8c" +
"R\x91\xee\xbd\xdc\xb7\x09q\x9f\x7f/\x15\x8b\x97\xde\xdd" +
"\xeb\xd5\x8c\x93U|U\xb5\xda\xfc\xce\x01\xa2\x8b\x93X" +
"\xca(\xb4\xdd\x05U\xccN\xb5\xb5\xd0\x9d\xd4\xaaI8" +
"\xd6\xac\xac\xba\xac\xfc\xf7$\xfeM%\xa83\x9c\xc4l" +
"[\xb7J\x05\xee\xe3$\xec6A\xb5\xd4b\x98\xe5$" +
"\x8e'\x1a\xa5?\x7f\x02\x10\xc79\x89S\x8c4\xe9y" +
"1%\xad\xe1\xb5$\xd6v\xab\x93\x96#}5\xd0\x0b" +
"3\xac\x8e\xd4\xe4\xd6\xa5W3\x1d\xe9P\xb0\xcd\xb4\xec" +
"\x86'\x91\x0c\xdd\x9f\x01\x00\x00\xff\xffR3\x9d#"
func init() {
schemas.Register(schema_db8274f9144abc7e,