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