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  }