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