Example #1
0
func main() {
	var config Config
	config.Backend.Type = "vxlan"
	if backend := os.Getenv("BACKEND"); backend != "" {
		config.Backend.Type = backend
	}
	config.Network = os.Getenv("NETWORK")
	if config.Network == "" {
		config.Network = "100.100.0.0/16"
	}
	flag.StringVar(&config.SubnetMin, "subnet-min", "", "container network min subnet")
	flag.StringVar(&config.SubnetMax, "subnet-max", "", "container network max subnet")
	flag.UintVar(&config.SubnetLen, "subnet-len", 0, "container network subnet length")
	flag.UintVar(&config.Backend.VNI, "vni", 0, "vxlan network identifier")
	flag.UintVar(&config.Backend.Port, "port", 0, "vxlan communication port (UDP)")
	flag.Parse()

	// wait for discoverd to come up
	status, err := cluster.WaitForHostStatus(os.Getenv("EXTERNAL_IP"), func(status *host.HostStatus) bool {
		return status.Discoverd != nil && status.Discoverd.URL != ""
	})
	if err != nil {
		log.Fatal(err)
	}

	// create service and config if not present
	client := discoverd.NewClientWithURL(status.Discoverd.URL)
	if err := client.AddService(serviceName, nil); err != nil && !hh.IsObjectExistsError(err) {
		log.Fatalf("error creating discoverd service: %s", err)
	}
	data, err := json.Marshal(map[string]Config{"config": config})
	if err != nil {
		log.Fatal(err)
	}
	err = client.Service(serviceName).SetMeta(&discoverd.ServiceMeta{Data: data})
	if err != nil && !hh.IsObjectExistsError(err) {
		log.Fatalf("error creating discoverd service metadata: %s", err)
	}

	flanneld, err := exec.LookPath("flanneld")
	if err != nil {
		log.Fatal(err)
	}

	if err := syscall.Exec(
		flanneld,
		[]string{
			flanneld,
			"-discoverd-url=" + status.Discoverd.URL,
			"-iface=" + os.Getenv("EXTERNAL_IP"),
			"-http-port=" + os.Getenv("PORT"),
			fmt.Sprintf("-notify-url=http://%s:1113/host/network", os.Getenv("EXTERNAL_IP")),
		},
		os.Environ(),
	); err != nil {
		log.Fatal(err)
	}
}
Example #2
0
func (s *EtcdSuite) TestSetMeta(c *C) {
	events := make(chan *discoverd.Event, 1)
	s.state.Subscribe("a", false, discoverd.EventKindServiceMeta, events)

	c.Assert(s.backend.AddService("a", nil), IsNil)
	c.Assert(s.backend.StartSync(), IsNil)

	// with service that doesn't exist
	err := s.backend.SetServiceMeta("b", &discoverd.ServiceMeta{Data: []byte("foo")})
	c.Assert(err, FitsTypeOf, NotFoundError{})

	// new with wrong index
	err = s.backend.SetServiceMeta("a", &discoverd.ServiceMeta{Data: []byte("foo"), Index: 1})
	c.Assert(hh.IsPreconditionFailedError(err), Equals, true)

	// new
	meta := &discoverd.ServiceMeta{Data: []byte("foo"), Index: 0}
	c.Assert(s.backend.SetServiceMeta("a", meta), IsNil)
	assertMetaEvent(c, events, "a", meta)

	// index=0 set with existing
	err = s.backend.SetServiceMeta("a", &discoverd.ServiceMeta{Data: []byte("foo"), Index: 0})
	c.Assert(hh.IsObjectExistsError(err), Equals, true)

	// set with existing, valid index
	meta.Data = []byte("bar")
	c.Assert(s.backend.SetServiceMeta("a", meta), IsNil)
	assertMetaEvent(c, events, "a", meta)

	// set with existing, low index
	meta.Index--
	meta.Data = []byte("baz")
	err = s.backend.SetServiceMeta("a", meta)
	c.Assert(hh.IsPreconditionFailedError(err), Equals, true)
}
Example #3
0
func (c *Client) maybeAddService(service string) error {
	err := c.AddService(service, nil)
	if hh.IsObjectExistsError(err) {
		return nil
	}
	return err
}
Example #4
0
// Run executes the program.
func (m *Main) Run() error {
	m.Logger.Info("running")

	// Read or generate the instance identifier from file.
	id, err := m.readID(filepath.Join(m.DataDir, "instance_id"))
	if err != nil {
		return err
	}
	m.Process.ID = id
	m.Process.DataDir = m.DataDir
	m.Process.Logger = m.Logger.New("component", "process", "id", id)

	// Start process.
	m.Logger.Info("starting process", "id", id)
	if err := m.Process.Start(); err != nil {
		m.Logger.Error("error starting process", "err", err)
		return err
	}

	// Add service to discoverd registry.
	m.Logger.Info("adding service", "name", m.ServiceName)
	if err = m.DiscoverdClient.AddService(m.ServiceName, nil); err != nil && !httphelper.IsObjectExistsError(err) {
		m.Logger.Error("error adding discoverd service", "err", err)
		return err
	}
	inst := &discoverd.Instance{
		Addr: ":" + m.Process.Port,
		Meta: map[string]string{"REDIS_ID": id},
	}

	// Register instance and retain heartbeater.
	m.Logger.Info("registering instance", "addr", inst.Addr, "meta", inst.Meta)
	hb, err := m.DiscoverdClient.RegisterInstance(m.ServiceName, inst)
	if err != nil {
		m.Logger.Error("error registering discoverd instance", "err", err)
		return err
	}
	m.hb = hb
	shutdown.BeforeExit(func() { hb.Close() })

	m.Logger.Info("opening port", "addr", m.Addr)

	// Open HTTP port.
	ln, err := net.Listen("tcp", m.Addr)
	if err != nil {
		m.Logger.Error("error opening port", "err", err)
		return err
	}
	m.ln = ln

	// Initialize and server handler.
	m.Logger.Info("serving http api")
	h := redis.NewHandler()
	h.Process = m.Process
	h.Heartbeater = m.hb
	h.Logger = m.Logger.New("component", "http")
	go func() { http.Serve(ln, h) }()

	return nil
}
Example #5
0
func main() {
	serviceName := os.Getenv("FLYNN_POSTGRES")
	if serviceName == "" {
		serviceName = "postgres"
	}
	singleton := os.Getenv("SINGLETON") == "true"
	password := os.Getenv("PGPASSWORD")

	const dataDir = "/data"
	idFile := filepath.Join(dataDir, "instance_id")
	idBytes, err := ioutil.ReadFile(idFile)
	if err != nil && !os.IsNotExist(err) {
		shutdown.Fatalf("error reading instance ID: %s", err)
	}
	id := string(idBytes)
	if len(id) == 0 {
		id = random.UUID()
		if err := ioutil.WriteFile(idFile, []byte(id), 0644); err != nil {
			shutdown.Fatalf("error writing instance ID: %s", err)
		}
	}

	err = discoverd.DefaultClient.AddService(serviceName, &discoverd.ServiceConfig{
		LeaderType: discoverd.LeaderTypeManual,
	})
	if err != nil && !httphelper.IsObjectExistsError(err) {
		shutdown.Fatal(err)
	}
	inst := &discoverd.Instance{
		Addr: ":5432",
		Meta: map[string]string{pgIdKey: id},
	}
	hb, err := discoverd.DefaultClient.RegisterInstance(serviceName, inst)
	if err != nil {
		shutdown.Fatal(err)
	}
	shutdown.BeforeExit(func() { hb.Close() })

	log := log15.New("app", "postgres")

	pg := NewPostgres(Config{
		ID:           id,
		Singleton:    singleton,
		DataDir:      filepath.Join(dataDir, "db"),
		BinDir:       "/usr/lib/postgresql/9.5/bin/",
		Password:     password,
		Logger:       log.New("component", "postgres"),
		ExtWhitelist: true,
		WaitUpstream: true,
		SHMType:      "posix",
	})
	dd := sd.NewDiscoverd(discoverd.DefaultClient.Service(serviceName), log.New("component", "discoverd"))

	peer := state.NewPeer(inst, id, pgIdKey, singleton, dd, pg, log.New("component", "peer"))
	shutdown.BeforeExit(func() { peer.Close() })

	go peer.Run()
	shutdown.Fatal(ServeHTTP(pg.(*Postgres), peer, hb, log.New("component", "http")))
	// TODO(titanous): clean shutdown of postgres
}
Example #6
0
func (c *Client) maybeAddService(service string) error {
	if err := c.AddService(service, nil); err != nil {
		if !hh.IsObjectExistsError(err) {
			return err
		}
	}
	return nil
}
Example #7
0
func (c *Client) AddService(name string, conf *ServiceConfig) error {
	if conf == nil {
		conf = &ServiceConfig{}
	}
	if conf.LeaderType == "" {
		conf.LeaderType = LeaderTypeOldest
	}
	return runAttempts.RunWithValidator(func() error {
		return c.Put("/services/"+name, conf, nil)
	}, func(err error) bool {
		// TODO(titanous): fix Retry error field to be correct for discoverd
		return !hh.IsObjectExistsError(err)
	})
}
Example #8
0
func main() {
	serviceName := os.Getenv("FLYNN_POSTGRES")
	if serviceName == "" {
		serviceName = "postgres"
	}
	singleton := os.Getenv("SINGLETON") == "true"
	password := os.Getenv("PGPASSWORD")

	err := discoverd.DefaultClient.AddService(serviceName, &discoverd.ServiceConfig{
		LeaderType: discoverd.LeaderTypeManual,
	})
	if err != nil && !httphelper.IsObjectExistsError(err) {
		shutdown.Fatal(err)
	}
	inst := &discoverd.Instance{Addr: ":5432"}
	hb, err := discoverd.DefaultClient.RegisterInstance(serviceName, inst)
	if err != nil {
		shutdown.Fatal(err)
	}
	shutdown.BeforeExit(func() { hb.Close() })

	log := log15.New("app", "postgres")

	pg := NewPostgres(Config{
		ID:           inst.ID,
		Singleton:    singleton,
		BinDir:       "/usr/lib/postgresql/9.4/bin/",
		Password:     password,
		Logger:       log.New("component", "postgres"),
		ExtWhitelist: true,
		WaitUpstream: true,
		// TODO(titanous) investigate this:
		SHMType: "sysv", // the default on 9.4, 'posix' is not currently supported in our containers
	})
	dd := NewDiscoverd(discoverd.DefaultClient.Service(serviceName), log.New("component", "discoverd"))

	peer := state.NewPeer(inst, singleton, dd, pg, log.New("component", "peer"))
	shutdown.BeforeExit(func() { peer.Close() })

	go peer.Run()
	shutdown.Fatal(ServeHTTP(pg.(*Postgres), peer, hb, log.New("component", "http")))
	// TODO(titanous): clean shutdown of postgres
}
Example #9
0
func monitor(port host.Port, container *ContainerInit, env map[string]string, log log15.Logger) (discoverd.Heartbeater, error) {
	config := port.Service
	client := discoverd.NewClientWithURL(env["DISCOVERD"])
	client.Logger = logger.New("component", "discoverd")

	if config.Create {
		// TODO: maybe reuse maybeAddService() from the client
		log.Info("creating service")
		if err := client.AddService(config.Name, nil); err != nil {
			if !hh.IsObjectExistsError(err) {
				log.Error("error creating service", "err", err)
				return nil, fmt.Errorf("something went wrong with discoverd: %s", err)
			}
		}
	}
	inst := &discoverd.Instance{
		Addr:  fmt.Sprintf("%s:%v", env["EXTERNAL_IP"], port.Port),
		Proto: port.Proto,
	}
	// add discoverd.EnvInstanceMeta if present
	for k, v := range env {
		if _, ok := discoverd.EnvInstanceMeta[k]; !ok {
			continue
		}
		if inst.Meta == nil {
			inst.Meta = make(map[string]string)
		}
		inst.Meta[k] = v
	}

	// no checker, but we still want to register a service
	if config.Check == nil {
		log.Info("registering instance", "instance", inst)
		return client.RegisterInstance(config.Name, inst)
	}

	var check health.Check
	switch config.Check.Type {
	case "tcp":
		check = &health.TCPCheck{Addr: inst.Addr}
	case "http", "https":
		check = &health.HTTPCheck{
			URL:        fmt.Sprintf("%s://%s%s", config.Check.Type, inst.Addr, config.Check.Path),
			Host:       config.Check.Host,
			StatusCode: config.Check.Status,
			MatchBytes: []byte(config.Check.Match),
		}
	default:
		// unsupported checker type
		return nil, fmt.Errorf("unsupported check type: %s", config.Check.Type)
	}
	log.Info("adding healthcheck", "type", config.Check.Type, "interval", config.Check.Interval, "threshold", config.Check.Threshold)
	reg := health.Registration{
		Registrar: client,
		Service:   config.Name,
		Instance:  inst,
		Monitor: health.Monitor{
			Interval:  config.Check.Interval,
			Threshold: config.Check.Threshold,
			Logger:    log.New("component", "monitor"),
		}.Run,
		Check:  check,
		Logger: log,
	}

	if config.Check.KillDown {
		reg.Events = make(chan health.MonitorEvent)
		go func() {
			if config.Check.StartTimeout == 0 {
				config.Check.StartTimeout = 10 * time.Second
			}

			start := false
			lastStatus := health.MonitorStatusDown
			var mtx sync.Mutex

			maybeKill := func() {
				if lastStatus == health.MonitorStatusDown {
					log.Warn("killing the job")
					container.Signal(int(syscall.SIGKILL), &struct{}{})
				}
			}
			go func() {
				// ignore events for the first StartTimeout interval
				<-time.After(config.Check.StartTimeout)
				mtx.Lock()
				defer mtx.Unlock()
				maybeKill() // check if the app is down
				start = true
			}()

			for e := range reg.Events {
				log.Info("got health monitor event", "status", e.Status)
				mtx.Lock()
				lastStatus = e.Status
				if !start {
					mtx.Unlock()
					continue
				}
				maybeKill()
				mtx.Unlock()
			}
		}()
	}
	return reg.Register(), nil
}
Example #10
0
func main() {
	serviceName := os.Getenv("FLYNN_MONGO")
	if serviceName == "" {
		serviceName = "mongodb"
	}
	singleton := os.Getenv("SINGLETON") == "true"
	password := os.Getenv("MONGO_PWD")
	httpPort := os.Getenv("HTTP_PORT")
	ip := os.Getenv("EXTERNAL_IP")
	if httpPort == "" {
		httpPort = "27018"
	}
	serverId := ipToId(net.ParseIP(ip))

	const dataDir = "/data"
	idFile := filepath.Join(dataDir, "instance_id")
	idBytes, err := ioutil.ReadFile(idFile)
	if err != nil && !os.IsNotExist(err) {
		shutdown.Fatalf("error reading instance ID: %s", err)
	}
	id := string(idBytes)
	if len(id) == 0 {
		id = random.UUID()
		if err := ioutil.WriteFile(idFile, []byte(id), 0644); err != nil {
			shutdown.Fatalf("error writing instance ID: %s", err)
		}
	}

	keyFile := filepath.Join(dataDir, "Keyfile")
	if err := ioutil.WriteFile(keyFile, []byte(password), 0600); err != nil {
		shutdown.Fatalf("error writing keyfile: %s", err)
	}

	err = discoverd.DefaultClient.AddService(serviceName, &discoverd.ServiceConfig{
		LeaderType: discoverd.LeaderTypeManual,
	})
	if err != nil && !httphelper.IsObjectExistsError(err) {
		shutdown.Fatal(err)
	}
	inst := &discoverd.Instance{
		Addr: ":" + mongodb.DefaultPort,
		Meta: map[string]string{mongoIdKey: id},
	}
	hb, err := discoverd.DefaultClient.RegisterInstance(serviceName, inst)
	if err != nil {
		shutdown.Fatal(err)
	}
	shutdown.BeforeExit(func() { hb.Close() })

	log := log15.New("app", "mongodb")

	process := mongodb.NewProcess()
	process.Password = password
	process.Singleton = singleton
	process.ServerID = serverId
	process.Host = ip

	dd := sd.NewDiscoverd(discoverd.DefaultClient.Service(serviceName), log.New("component", "discoverd"))

	peer := state.NewPeer(inst, id, mongoIdKey, singleton, dd, process, log.New("component", "peer"))
	shutdown.BeforeExit(func() { peer.Close() })

	go peer.Run()

	handler := mongodb.NewHandler()
	handler.Process = process
	handler.Peer = peer
	handler.Heartbeater = hb
	handler.Logger = log.New("component", "http")

	shutdown.Fatal(http.ListenAndServe(":"+httpPort, handler))
}