forked from willbryant/verm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverm.go
137 lines (116 loc) · 4.77 KB
/
verm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package main
import "flag"
import "fmt"
import "net"
import "net/http"
import "os"
import "os/signal"
import "runtime"
import "runtime/pprof"
import "strings"
import "time"
import "syscall"
import "github.com/willbryant/verm/mimeext"
var compiled_version string
var compiled_root_data_directory string
func banner() string {
if compiled_version != "" {
return "Verm " + compiled_version
} else {
return "Verm"
}
}
func default_root() string {
if compiled_root_data_directory != "" {
return compiled_root_data_directory
} else {
return DefaultRoot
}
}
func main() {
var rootDataDirectory, listenAddress, port, mimeTypesFile string
var mimeTypesClear bool
var replicationTargets ReplicationTargets
var replicationWorkers int
var healthCheckPath, healthyIfFile, healthyUnlessFile string
var quiet bool
flag.StringVar(&rootDataDirectory, "data", default_root(), "Sets the root data directory to /foo. Must be fully-qualified (ie. it must start with a /).")
flag.StringVar(&listenAddress, "listen", DefaultListenAddress, "Listen on the given IP address. Default: listen on all network interfaces.")
flag.StringVar(&port, "port", DefaultPort, "Listen on the given port.")
flag.StringVar(&mimeTypesFile, "mime-types-file", DefaultMimeTypesFile, "Load MIME content-types from the given file.")
flag.BoolVar(&mimeTypesClear, "no-default-mime-types", false, "Clear the built-in MIME types so the settings in the file given in the mime-types-file option are used exclusively.")
flag.Var(&replicationTargets, "replicate-to", "Replicate files to the given Verm server. May be given multiple times.")
flag.IntVar(&replicationWorkers, "replication-workers", runtime.NumCPU()*10, "Number of gophers to use to replicate files to each Verm server. Generally should be large; the default scales with the number of CPUs detected.")
flag.BoolVar(&quiet, "quiet", false, "Quiet mode. Don't print startup/shutdown/request log messages to stdout.")
flag.StringVar(&healthCheckPath, "health-check-path", "", "Treat requests to this path as health checks from your load balancer, and give a 200 response without trying to serve a file.")
flag.StringVar(&healthyIfFile, "healthy-if-file", "", "Respond to requests to the health-check-path with a 503 response code if this file doesn't exist.")
flag.StringVar(&healthyUnlessFile, "healthy-unless-file", "", "Respond to requests to the health-check-path with a 503 response code if this file exists.")
flag.VisitAll(setFlagFromEnvironment)
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU())
mimeext.LoadMimeFile(mimeTypesFile, mimeTypesClear)
listener, err := net.Listen("tcp", listenAddress+":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't listen on %s:%s: %s\n", listenAddress, port, err.Error())
os.Exit(1)
} else if !quiet {
fmt.Fprintf(os.Stdout, "%s listening on http://%s:%s, data in %s\n", banner(), listenAddress, port, rootDataDirectory)
}
statistics := NewLogStatistics()
server := VermServer(listener, rootDataDirectory, &replicationTargets, statistics, quiet)
replicationTargets.Start(rootDataDirectory, statistics, replicationWorkers)
replicationTargets.EnqueueResync()
done := make(chan interface{})
go waitForSignals(&server, &replicationTargets, done)
if healthCheckPath != "" {
http.Handle(AddRoot(healthCheckPath), HealthCheckServer(healthyIfFile, healthyUnlessFile))
}
http.Handle("/", server)
err = server.Serve()
// emulate the solution used by go 1.8+ to return ErrServerClosed
select {
case <-done:
// ignore the error, as we always get 'accept tcp <host and port>: use of closed network connection'
default:
// Serve returned without waitForSignals signalling us, so there's a real error condition
if err != nil {
fmt.Fprintf(os.Stderr, "Unexpected error: %s\n", err.Error())
os.Exit(1)
}
}
server.Tracker.Shutdown(ShutdownResponseTimeout * time.Second)
os.Exit(0)
}
func waitForSignals(server *vermServer, targets *ReplicationTargets, done chan interface{}) {
signals := make(chan os.Signal, 4)
signal.Notify(signals, syscall.SIGINT)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGUSR1)
signal.Notify(signals, syscall.SIGUSR2)
closed := false
for {
switch <-signals {
case syscall.SIGUSR1:
fmt.Fprintf(os.Stdout, "Resyncing by request\n")
targets.EnqueueResync()
case syscall.SIGUSR2:
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
pprof.Lookup("heap").WriteTo(os.Stdout, 1)
case syscall.SIGINT, syscall.SIGTERM:
if !server.Quiet {
fmt.Fprintf(os.Stdout, "Verm shutting down by request\n")
}
if !closed {
closed = true
close(done)
server.Shutdown()
}
}
}
}
func setFlagFromEnvironment(f *flag.Flag) {
env := "VERM_" + strings.Replace(strings.ToUpper(f.Name), "-", "_", -1)
if os.Getenv(env) != "" {
flag.Set(f.Name, os.Getenv(env))
}
}