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  }