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  }