diff --git a/cmd/daemon.go b/cmd/daemon.go index 4398cc7..afa83d3 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -11,6 +11,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/gianarb/orbiter/api" + "github.com/gianarb/orbiter/autoscaler" "github.com/gianarb/orbiter/core" ) @@ -25,25 +26,39 @@ func (c *DaemonCmd) Run(args []string) int { var debug bool cmdFlags := flag.NewFlagSet("event", flag.ExitOnError) cmdFlags.StringVar(&port, "port", ":8000", "port") - cmdFlags.StringVar(&configPath, "config", "/etc/deamon.yml", "config") + cmdFlags.StringVar(&configPath, "config", "", "config") cmdFlags.BoolVar(&debug, "debug", false, "debug") if err := cmdFlags.Parse(args); err != nil { logrus.WithField("error", err).Warn("Problem to parse arguments.") - return 1 + os.Exit(1) } if debug == true { logrus.SetLevel(logrus.DebugLevel) logrus.Debug("Daemon started in debug mode") } - config, err := readConfiguration(configPath) - if err != nil { - logrus.WithField("error", err).Warn("Configuration file malformed.") - return 1 + coreEngine := core.Core{ + Autoscalers: autoscaler.Autoscalers{}, } - core, err := core.NewCore(config.AutoscalersConf) - if err != nil { - logrus.WithField("error", err).Warn(err) - return 1 + var err error + if configPath != "" { + config, err := readConfiguration(configPath) + if err != nil { + logrus.WithField("error", err).Warn("Configuration file malformed.") + os.Exit(1) + } + logrus.Infof("Starting from configuration file located %s", configPath) + err = core.NewCoreByConfig(config.AutoscalersConf, &coreEngine) + if err != nil { + logrus.WithField("error", err).Warn(err) + os.Exit(1) + } + } else { + logrus.Info("Starting in auto-detection mode.") + err = core.Autodetect(&coreEngine) + if err != nil { + logrus.WithField("error", err).Info(err) + os.Exit(0) + } } go func() { sigchan := make(chan os.Signal, 10) @@ -52,7 +67,7 @@ func (c *DaemonCmd) Run(args []string) int { logrus.Info("Stopping and cleaning. Bye!") os.Exit(0) }() - router := api.GetRouter(core, c.EventChannel) + router := api.GetRouter(coreEngine, c.EventChannel) logrus.Infof("API Server run on port %s", port) http.ListenAndServe(port, router) return 0 diff --git a/core/autodetect.go b/core/autodetect.go new file mode 100644 index 0000000..dcd448e --- /dev/null +++ b/core/autodetect.go @@ -0,0 +1,81 @@ +package core + +import ( + "context" + "errors" + "fmt" + "strconv" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/client" + "github.com/gianarb/orbiter/autoscaler" + "github.com/gianarb/orbiter/provider" +) + +// This function use diferent strategies to get information from +// the system itself to configure the autoloader. +// They can be environment variables for example or other systems. +func Autodetect(core *Core) error { + autoDetectSwarmMode(core) + if len(core.Autoscalers) == 0 { + return errors.New("we didn't detect any autoscaling group") + } + return nil +} + +func autoDetectSwarmMode(c *Core) { + ctx := context.Background() + dockerClient, err := client.NewEnvClient() + if err != nil { + logrus.WithField("error", err).Debug("Problem communication with Docker") + return + } + info, err := dockerClient.Info(ctx) + if err != nil { + logrus.WithField("error", err).Debug("We didn't detect any Docker Swarm running") + return + } + if info.Swarm.NodeID == "" { + logrus.Debug("We didn't detect any Docker Swarm running") + return + } + services, err := dockerClient.ServiceList(ctx, types.ServiceListOptions{}) + if err != nil { + logrus.WithField("error", err).Debug("Bad comunication with Docker.") + return + } + prov, _ := provider.NewSwarmProvider(map[string]string{}) + for _, service := range services { + s, err := getAutoscalerByService(prov, service.Spec.Annotations) + if err != nil { + continue + } + logrus.Debugf("autodetect_swarm/%s added to orbiter.", service.Spec.Annotations.Name) + c.Autoscalers[fmt.Sprintf("autodetect_swarm/%s", service.Spec.Annotations.Name)] = s + } +} + +func getAutoscalerByService(p autoscaler.Provider, an swarm.Annotations) (autoscaler.Autoscaler, error) { + _, e := an.Labels["orbiter"] + if e == false { + return autoscaler.Autoscaler{}, errors.New("") + } + up := convertStringLabelToInt("orbiter.up", an.Labels) + down := convertStringLabelToInt("orbiter.down", an.Labels) + as := autoscaler.NewAutoscaler(p, an.Name, up, down) + return as, nil +} + +func convertStringLabelToInt(labelName string, labels map[string]string) int { + row, e := labels[labelName] + if e == false { + i, err := strconv.ParseInt(row, 10, 64) + if err != nil { + return 1 + } + return int(i) + } + return 1 +} diff --git a/core/daemon.go b/core/daemon.go index 1fefe66..c1b9632 100644 --- a/core/daemon.go +++ b/core/daemon.go @@ -11,20 +11,17 @@ type Core struct { Autoscalers autoscaler.Autoscalers } -func NewCore(c map[string]AutoscalerConf) (Core, error) { +func NewCoreByConfig(c map[string]AutoscalerConf, core *Core) error { scalers := autoscaler.Autoscalers{} - var core Core for scalerName, scaler := range c { p, err := provider.NewProvider(scaler.Provider, scaler.Parameters) if err != nil { - return core, err + return err } for serviceName, policy := range scaler.Policies { scalers[fmt.Sprintf("%s/%s", scalerName, serviceName)] = autoscaler.NewAutoscaler(p, serviceName, policy.Up, policy.Down) } } - core = Core{ - Autoscalers: scalers, - } - return core, nil + core.Autoscalers = scalers + return nil } diff --git a/core/daemon_test.go b/core/daemon_test.go index 5a588d1..a48868a 100644 --- a/core/daemon_test.go +++ b/core/daemon_test.go @@ -1,8 +1,15 @@ package core -import "testing" +import ( + "testing" + + "github.com/gianarb/orbiter/autoscaler" +) func TestNewCore(t *testing.T) { + core := Core{ + Autoscalers: autoscaler.Autoscalers{}, + } conf := map[string]AutoscalerConf{ "first-scaler": AutoscalerConf{ Provider: "fake", @@ -29,7 +36,7 @@ func TestNewCore(t *testing.T) { }, }, } - core, err := NewCore(conf) + err := NewCoreByConfig(conf, &core) if err != nil { t.Fatal(err) } @@ -39,6 +46,9 @@ func TestNewCore(t *testing.T) { } func TestGetSingleAutoscaler(t *testing.T) { + core := Core{ + Autoscalers: autoscaler.Autoscalers{}, + } conf := map[string]AutoscalerConf{ "second": AutoscalerConf{ Provider: "fake", @@ -55,7 +65,7 @@ func TestGetSingleAutoscaler(t *testing.T) { }, }, } - core, _ := NewCore(conf) + NewCoreByConfig(conf, &core) _, ok := core.Autoscalers["second/micro"] if ok == false { t.Fatal("micro exist") @@ -63,6 +73,9 @@ func TestGetSingleAutoscaler(t *testing.T) { } func TestNewCoreWithUnsupportedProvider(t *testing.T) { + core := Core{ + Autoscalers: autoscaler.Autoscalers{}, + } conf := map[string]AutoscalerConf{ "second-scaler": AutoscalerConf{ Provider: "fake", @@ -89,7 +102,7 @@ func TestNewCoreWithUnsupportedProvider(t *testing.T) { }, }, } - _, err := NewCore(conf) + err := NewCoreByConfig(conf, &core) if err.Error() != "lalala not supported." { t.Fatal(err) }