github.com/amrnt/deis@v1.3.1/router/boot.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "net" 7 "os" 8 "os/exec" 9 "os/signal" 10 "syscall" 11 "time" 12 13 "github.com/ActiveState/tail" 14 "github.com/Sirupsen/logrus" 15 "github.com/coreos/go-etcd/etcd" 16 17 "github.com/deis/deis/router/logger" 18 ) 19 20 var log = logrus.New() 21 22 const ( 23 timeout time.Duration = 10 * time.Second 24 ttl time.Duration = timeout * 2 25 gitLogFile string = "/opt/nginx/logs/git.log" 26 nginxAccessLog string = "/opt/nginx/logs/access.log" 27 nginxErrorLog string = "/opt/nginx/logs/error.log" 28 ) 29 30 func main() { 31 log.Formatter = new(logger.StdOutFormatter) 32 33 logLevel := getopt("LOG", "info") 34 if level, err := logrus.ParseLevel(logLevel); err == nil { 35 log.Level = level 36 } 37 38 log.Debug("reading environment variables...") 39 host := getopt("HOST", "127.0.0.1") 40 41 etcdPort := getopt("ETCD_PORT", "4001") 42 43 etcdPath := getopt("ETCD_PATH", "/deis/router") 44 45 hostEtcdPath := getopt("HOST_ETCD_PATH", "/deis/router/hosts/"+host) 46 47 externalPort := getopt("EXTERNAL_PORT", "80") 48 49 client := etcd.NewClient([]string{"http://" + host + ":" + etcdPort}) 50 51 // wait until etcd has discarded potentially stale values 52 time.Sleep(timeout + 1) 53 54 log.Debug("creating required defaults in etcd...") 55 mkdirEtcd(client, "/deis/controller") 56 mkdirEtcd(client, "/deis/services") 57 mkdirEtcd(client, "/deis/domains") 58 mkdirEtcd(client, "/deis/builder") 59 mkdirEtcd(client, "/deis/router/hosts") 60 61 setDefaultEtcd(client, etcdPath+"/gzip", "on") 62 63 log.Info("Starting Nginx...") 64 // tail the logs 65 go tailFile(nginxAccessLog) 66 go tailFile(nginxErrorLog) 67 go tailFile(gitLogFile) 68 69 nginxChan := make(chan bool) 70 go launchNginx(nginxChan) 71 <-nginxChan 72 73 waitForInitialConfd(host+":"+etcdPort, timeout) 74 75 go launchConfd(host + ":" + etcdPort) 76 77 go publishService(client, hostEtcdPath, host, externalPort, uint64(ttl.Seconds())) 78 79 log.Info("deis-router running...") 80 81 exitChan := make(chan os.Signal, 2) 82 signal.Notify(exitChan, syscall.SIGTERM, syscall.SIGINT) 83 go cleanOnExit(exitChan) 84 <-exitChan 85 } 86 87 func cleanOnExit(exitChan chan os.Signal) { 88 for _ = range exitChan { 89 tail.Cleanup() 90 } 91 } 92 93 // Wait until the compilation of the templates 94 func waitForInitialConfd(etcd string, timeout time.Duration) { 95 for { 96 var buffer bytes.Buffer 97 output := bufio.NewWriter(&buffer) 98 log.Debugf("Connecting to etcd server %s", etcd) 99 cmd := exec.Command("confd", "-onetime", "-node", etcd, "-config-file", "/app/confd.toml", "-debug", "-verbose") 100 cmd.Stdout = output 101 cmd.Stderr = output 102 103 err := cmd.Run() 104 output.Flush() 105 if err == nil { 106 break 107 } 108 109 log.Info("waiting for confd to write initial templates...") 110 log.Debugf("\n%s", buffer.String()) 111 time.Sleep(timeout) 112 } 113 } 114 115 func launchConfd(etcd string) { 116 cmd := exec.Command("confd", "-node", etcd, "-config-file", "/app/confd.toml") 117 cmd.Stdout = os.Stdout 118 cmd.Stderr = os.Stderr 119 120 if err := cmd.Start(); err != nil { 121 log.Warn("confd terminated by error: %v", err) 122 } 123 } 124 125 func launchNginx(nginxChan chan bool) { 126 cmd := exec.Command("/opt/nginx/sbin/nginx", "-c", "/opt/nginx/conf/nginx.conf") 127 cmd.Stdout = os.Stdout 128 cmd.Stderr = os.Stderr 129 130 if err := cmd.Start(); err != nil { 131 log.Warn("Nginx terminated by error: %v", err) 132 } 133 134 // Wait until the nginx is available 135 for { 136 _, err := net.DialTimeout("tcp", "127.0.0.1:80", timeout) 137 if err == nil { 138 nginxChan <- true 139 break 140 } 141 } 142 143 if err := cmd.Wait(); err != nil { 144 log.Warnf("Nginx terminated by error: %v", err) 145 } else { 146 log.Info("Reloading nginx (change in configuration)...") 147 } 148 } 149 150 func tailFile(path string) { 151 mkfifo(path) 152 t, _ := tail.TailFile(path, tail.Config{Follow: true}) 153 154 for line := range t.Lines { 155 log.Info(line.Text) 156 } 157 } 158 159 func mkfifo(path string) { 160 os.Remove(path) 161 if err := syscall.Mkfifo(path, syscall.S_IFIFO|0666); err != nil { 162 log.Fatalf("%v", err) 163 } 164 } 165 166 func publishService( 167 client *etcd.Client, 168 etcdPath string, 169 host string, 170 externalPort string, 171 ttl uint64) { 172 173 for { 174 setEtcd(client, etcdPath, host+":"+externalPort, ttl) 175 time.Sleep(timeout) 176 } 177 } 178 179 func setEtcd(client *etcd.Client, key, value string, ttl uint64) { 180 _, err := client.Set(key, value, ttl) 181 if err != nil { 182 log.Warn(err) 183 } 184 } 185 186 func setDefaultEtcd(client *etcd.Client, key, value string) { 187 _, err := client.Set(key, value, 0) 188 if err != nil { 189 log.Warn(err) 190 } 191 } 192 193 func mkdirEtcd(client *etcd.Client, path string) { 194 _, err := client.CreateDir(path, 0) 195 if err != nil { 196 log.Warn(err) 197 } 198 } 199 200 func getopt(name, dfault string) string { 201 value := os.Getenv(name) 202 if value == "" { 203 value = dfault 204 } 205 return value 206 }