github.com/jiasir/deis@v1.12.2/router/cmd/boot/boot.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "net" 7 "os" 8 "os/exec" 9 "os/signal" 10 "strings" 11 "syscall" 12 "time" 13 14 "github.com/ActiveState/tail" 15 "github.com/Sirupsen/logrus" 16 "github.com/coreos/go-etcd/etcd" 17 18 "github.com/deis/deis/router/logger" 19 ) 20 21 var log = logrus.New() 22 23 const ( 24 timeout time.Duration = 10 * time.Second 25 ttl time.Duration = timeout * 2 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/config") 56 mkdirEtcd(client, "/deis/controller") 57 mkdirEtcd(client, "/deis/services") 58 mkdirEtcd(client, "/deis/domains") 59 mkdirEtcd(client, "/deis/builder") 60 mkdirEtcd(client, "/deis/certs") 61 mkdirEtcd(client, "/deis/router/hosts") 62 mkdirEtcd(client, "/deis/router/hsts") 63 mkdirEtcd(client, "/registry/services/specs/default") 64 65 setDefaultEtcd(client, etcdPath+"/gzip", "on") 66 67 log.Info("Starting Nginx...") 68 69 go tailFile(nginxAccessLog) 70 go tailFile(nginxErrorLog) 71 72 nginxChan := make(chan bool) 73 go launchNginx(nginxChan) 74 <-nginxChan 75 76 // FIXME: have to launch cron first so generate-certs will generate the files nginx requires 77 go launchCron() 78 79 waitForInitialConfd(host+":"+etcdPort, timeout) 80 81 go launchConfd(host + ":" + etcdPort) 82 83 go publishService(client, hostEtcdPath, host, externalPort, uint64(ttl.Seconds())) 84 85 log.Info("deis-router running...") 86 87 exitChan := make(chan os.Signal, 2) 88 signal.Notify(exitChan, syscall.SIGTERM, syscall.SIGINT) 89 <-exitChan 90 tail.Cleanup() 91 } 92 93 func launchCron() { 94 // edit crontab 95 crontab := `(echo "* * * * * generate-certs >> /dev/stdout") | crontab -` 96 cmd := exec.Command("bash", "-c", crontab) 97 if err := cmd.Run(); err != nil { 98 log.Fatalf("could not write to crontab: %v", err) 99 } 100 101 // run cron 102 cmd = exec.Command("crond") 103 if err := cmd.Run(); err != nil { 104 log.Fatalf("cron terminated by error: %v", err) 105 } 106 } 107 108 // Wait until the compilation of the templates 109 func waitForInitialConfd(etcd string, timeout time.Duration) { 110 for { 111 var buffer bytes.Buffer 112 output := bufio.NewWriter(&buffer) 113 log.Debugf("Connecting to etcd server %s", etcd) 114 cmd := exec.Command("confd", "-node", etcd, "-onetime", "--log-level", "error") 115 cmd.Stdout = output 116 cmd.Stderr = output 117 118 err := cmd.Run() 119 output.Flush() 120 if err == nil { 121 break 122 } 123 124 log.Info("waiting for confd to write initial templates...") 125 log.Debugf("\n%s", buffer.String()) 126 time.Sleep(timeout) 127 } 128 } 129 130 func launchConfd(etcd string) { 131 cmd := exec.Command("confd", "-node", etcd, "--log-level", "error", "--interval", "5") 132 cmd.Stdout = os.Stdout 133 cmd.Stderr = os.Stderr 134 135 if err := cmd.Start(); err != nil { 136 log.Warn("confd terminated by error: %v", err) 137 } 138 } 139 140 func launchNginx(nginxChan chan bool) { 141 cmd := exec.Command("/opt/nginx/sbin/nginx", "-c", "/opt/nginx/conf/nginx.conf") 142 cmd.Stdout = os.Stdout 143 cmd.Stderr = os.Stderr 144 145 if err := cmd.Start(); err != nil { 146 log.Warn("Nginx terminated by error: %v", err) 147 } 148 149 // Wait until the nginx is available 150 for { 151 _, err := net.DialTimeout("tcp", "127.0.0.1:80", timeout) 152 if err == nil { 153 nginxChan <- true 154 break 155 } 156 } 157 158 if err := cmd.Wait(); err != nil { 159 log.Warnf("Nginx terminated by error: %v", err) 160 } else { 161 log.Info("Reloading nginx (change in configuration)...") 162 } 163 } 164 165 func mkfifo(path string) { 166 os.Remove(path) 167 if err := syscall.Mkfifo(path, syscall.S_IFIFO|0666); err != nil { 168 log.Fatalf("%v", err) 169 } 170 } 171 172 func tailFile(path string) { 173 mkfifo(path) 174 t, _ := tail.TailFile(path, tail.Config{Follow: true}) 175 176 for line := range t.Lines { 177 log.Info(line.Text) 178 } 179 } 180 181 func publishService( 182 client *etcd.Client, 183 etcdPath string, 184 host string, 185 externalPort string, 186 ttl uint64) { 187 188 for { 189 setEtcd(client, etcdPath, host+":"+externalPort, ttl) 190 time.Sleep(timeout) 191 } 192 } 193 194 func setEtcd(client *etcd.Client, key, value string, ttl uint64) { 195 _, err := client.Set(key, value, ttl) 196 if err != nil { 197 log.Warn(err) 198 } 199 } 200 201 func setDefaultEtcd(client *etcd.Client, key, value string) { 202 _, err := client.Set(key, value, 0) 203 if err != nil { 204 log.Warn(err) 205 } 206 } 207 208 func mkdirEtcd(client *etcd.Client, path string) { 209 _, err := client.CreateDir(path, 0) 210 if err != nil && !strings.Contains(err.Error(), "Key already exists") { 211 log.Warn(err) 212 } 213 } 214 215 func getopt(name, dfault string) string { 216 value := os.Getenv(name) 217 if value == "" { 218 value = dfault 219 } 220 return value 221 }