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  }