main.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // Copyright (C) 2018 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "context"
  9. "crypto/tls"
  10. "log"
  11. "net/http"
  12. _ "net/http/pprof"
  13. "os"
  14. "os/signal"
  15. "runtime"
  16. "time"
  17. "github.com/alecthomas/kong"
  18. "github.com/prometheus/client_golang/prometheus/promhttp"
  19. "github.com/thejerf/suture/v4"
  20. "github.com/syncthing/syncthing/internal/blob"
  21. "github.com/syncthing/syncthing/internal/blob/azureblob"
  22. "github.com/syncthing/syncthing/internal/blob/s3"
  23. _ "github.com/syncthing/syncthing/lib/automaxprocs"
  24. "github.com/syncthing/syncthing/lib/build"
  25. "github.com/syncthing/syncthing/lib/protocol"
  26. "github.com/syncthing/syncthing/lib/rand"
  27. "github.com/syncthing/syncthing/lib/tlsutil"
  28. )
  29. const (
  30. addressExpiryTime = 2 * time.Hour
  31. databaseStatisticsInterval = 5 * time.Minute
  32. // Reannounce-After is set to reannounceAfterSeconds +
  33. // random(reannounzeFuzzSeconds), similar for Retry-After
  34. reannounceAfterSeconds = 3300
  35. reannounzeFuzzSeconds = 300
  36. errorRetryAfterSeconds = 1500
  37. errorRetryFuzzSeconds = 300
  38. // Retry for not found is notFoundRetrySeenSeconds for records we have
  39. // seen an announcement for (but it's not active right now) and
  40. // notFoundRetryUnknownSeconds for records we have never seen (or not
  41. // seen within the last week).
  42. notFoundRetryUnknownMinSeconds = 60
  43. notFoundRetryUnknownMaxSeconds = 3600
  44. httpReadTimeout = 5 * time.Second
  45. httpWriteTimeout = 5 * time.Second
  46. httpMaxHeaderBytes = 1 << 10
  47. // Size of the replication outbox channel
  48. replicationOutboxSize = 10000
  49. )
  50. var debug = false
  51. type CLI struct {
  52. Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
  53. Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"`
  54. HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"`
  55. Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"`
  56. Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
  57. MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"`
  58. DesiredNotFoundRate float64 `group:"Listen" help:"Desired maximum rate of not-found replies (/s)" default:"1000"`
  59. DBDir string `group:"Database" help:"Database directory" default:"." env:"DISCOVERY_DB_DIR"`
  60. DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"`
  61. DBS3Endpoint string `name:"db-s3-endpoint" group:"Database (S3 backup)" hidden:"true" help:"S3 endpoint for database" env:"DISCOVERY_DB_S3_ENDPOINT"`
  62. DBS3Region string `name:"db-s3-region" group:"Database (S3 backup)" hidden:"true" help:"S3 region for database" env:"DISCOVERY_DB_S3_REGION"`
  63. DBS3Bucket string `name:"db-s3-bucket" group:"Database (S3 backup)" hidden:"true" help:"S3 bucket for database" env:"DISCOVERY_DB_S3_BUCKET"`
  64. DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"`
  65. DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"`
  66. DBAzureBlobAccount string `name:"db-azure-blob-account" env:"DISCOVERY_DB_AZUREBLOB_ACCOUNT"`
  67. DBAzureBlobKey string `name:"db-azure-blob-key" env:"DISCOVERY_DB_AZUREBLOB_KEY"`
  68. DBAzureBlobContainer string `name:"db-azure-blob-container" env:"DISCOVERY_DB_AZUREBLOB_CONTAINER"`
  69. AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"`
  70. Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
  71. Version bool `short:"v" help:"Print version and exit"`
  72. }
  73. func main() {
  74. log.SetOutput(os.Stdout)
  75. var cli CLI
  76. kong.Parse(&cli)
  77. debug = cli.Debug
  78. log.Println(build.LongVersionFor("stdiscosrv"))
  79. if cli.Version {
  80. return
  81. }
  82. buildInfo.WithLabelValues(build.Version, runtime.Version(), build.User, build.Date.UTC().Format("2006-01-02T15:04:05Z")).Set(1)
  83. var cert tls.Certificate
  84. if !cli.HTTP {
  85. var err error
  86. cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
  87. if os.IsNotExist(err) {
  88. log.Println("Failed to load keypair. Generating one, this might take a while...")
  89. cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365, false)
  90. if err != nil {
  91. log.Fatalln("Failed to generate X509 key pair:", err)
  92. }
  93. } else if err != nil {
  94. log.Fatalln("Failed to load keypair:", err)
  95. }
  96. devID := protocol.NewDeviceID(cert.Certificate[0])
  97. log.Println("Server device ID is", devID)
  98. }
  99. // Root of the service tree.
  100. main := suture.New("main", suture.Spec{
  101. PassThroughPanics: true,
  102. Timeout: 2 * time.Minute,
  103. })
  104. // If configured, use blob storage for database backups.
  105. var blobs blob.Store
  106. var err error
  107. if cli.DBS3Endpoint != "" {
  108. blobs, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
  109. } else if cli.DBAzureBlobAccount != "" {
  110. blobs, err = azureblob.NewBlobStore(cli.DBAzureBlobAccount, cli.DBAzureBlobKey, cli.DBAzureBlobContainer)
  111. }
  112. if err != nil {
  113. log.Fatalf("Failed to create blob store: %v", err)
  114. }
  115. // Start the database.
  116. db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, blobs)
  117. main.Add(db)
  118. // If we have an AMQP broker for replication, start that
  119. var repl replicator
  120. if cli.AMQPAddress != "" {
  121. clientID := rand.String(10)
  122. kr := newAMQPReplicator(cli.AMQPAddress, clientID, db)
  123. main.Add(kr)
  124. repl = kr
  125. }
  126. // Start the main API server.
  127. qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression, cli.DesiredNotFoundRate)
  128. main.Add(qs)
  129. // If we have a metrics port configured, start a metrics handler.
  130. if cli.MetricsListen != "" {
  131. go func() {
  132. mux := http.NewServeMux()
  133. mux.Handle("/metrics", promhttp.Handler())
  134. log.Fatal(http.ListenAndServe(cli.MetricsListen, mux))
  135. }()
  136. }
  137. ctx, cancel := context.WithCancel(context.Background())
  138. defer cancel()
  139. // Cancel on signal
  140. signalChan := make(chan os.Signal, 1)
  141. signal.Notify(signalChan, os.Interrupt)
  142. go func() {
  143. sig := <-signalChan
  144. log.Printf("Received signal %s; shutting down", sig)
  145. cancel()
  146. }()
  147. // Engage!
  148. main.Serve(ctx)
  149. }