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) } }
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) }
func (c *Client) maybeAddService(service string) error { err := c.AddService(service, nil) if hh.IsObjectExistsError(err) { return nil } return err }
// 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 }
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 }
func (c *Client) maybeAddService(service string) error { if err := c.AddService(service, nil); err != nil { if !hh.IsObjectExistsError(err) { return err } } return nil }
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) }) }
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 }
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 }
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)) }