diff --git a/Gopkg.lock b/Gopkg.lock index bd2805912..a69be902a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -977,9 +977,12 @@ "pkg/apis/metrics/v1alpha1", "pkg/apis/metrics/v1beta1", "pkg/client/clientset_generated/clientset", + "pkg/client/clientset_generated/clientset/fake", "pkg/client/clientset_generated/clientset/scheme", "pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1", + "pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1/fake", "pkg/client/clientset_generated/clientset/typed/metrics/v1beta1", + "pkg/client/clientset_generated/clientset/typed/metrics/v1beta1/fake", ] pruneopts = "" revision = "7afb501849915187f5b29c9727e106cbc6299d1c" @@ -1034,6 +1037,7 @@ "google.golang.org/grpc", "gopkg.in/yaml.v2", "k8s.io/api/core/v1", + "k8s.io/apimachinery/pkg/api/resource", "k8s.io/apimachinery/pkg/apis/meta/v1", "k8s.io/apimachinery/pkg/fields", "k8s.io/apimachinery/pkg/labels", @@ -1046,8 +1050,9 @@ "k8s.io/client-go/rest", "k8s.io/client-go/testing", "k8s.io/client-go/tools/clientcmd", - "k8s.io/metrics/pkg/apis/metrics", + "k8s.io/metrics/pkg/apis/metrics/v1beta1", "k8s.io/metrics/pkg/client/clientset_generated/clientset", + "k8s.io/metrics/pkg/client/clientset_generated/clientset/fake", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/api/response_time_middleware.go b/api/response_time_middleware.go index 5911968ed..b6d0a953d 100644 --- a/api/response_time_middleware.go +++ b/api/response_time_middleware.go @@ -44,7 +44,7 @@ func (m *ResponseTimeMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Reques m.next.ServeHTTP(writerWrapper, r) routeName, _ := mux.CurrentRoute(r).GetPathTemplate() - reporters.Report(reportersConstants.EventHTTPResponseTime, map[string]string{ + reporters.Report(reportersConstants.EventHTTPResponseTime, map[string]interface{}{ reportersConstants.TagRoute: routeName, reportersConstants.TagResponseTime: time.Now().Sub(start).String(), reportersConstants.TagHTTPStatus: writerWrapper.Status(), diff --git a/api/scheduler_handler.go b/api/scheduler_handler.go index 68c715170..b7a69fdf8 100644 --- a/api/scheduler_handler.go +++ b/api/scheduler_handler.go @@ -16,6 +16,8 @@ import ( "time" maestroErrors "github.com/topfreegames/maestro/errors" + "github.com/topfreegames/maestro/reporters" + reportersConstants "github.com/topfreegames/maestro/reporters/constants" yaml "gopkg.in/yaml.v2" "github.com/sirupsen/logrus" @@ -182,6 +184,11 @@ func (g *SchedulerUpdateHandler) update( "status": status, }).Error("error updating scheduler config") } + reporters.Report(reportersConstants.EventSchedulerUpdate, map[string]interface{}{ + "name": configYaml.Name, + "game": configYaml.Game, + "error": err, + }) finishOpErr := mr.WithSegment(models.SegmentPipeExec, func() error { return operationManager.Finish(status, description, err) }) diff --git a/eventforwarder/forward.go b/eventforwarder/forward.go index ca7fcb02c..d7c1ed256 100644 --- a/eventforwarder/forward.go +++ b/eventforwarder/forward.go @@ -313,7 +313,7 @@ func reportRPCStatus( game := scheduler.ConfigYAML.Game - status := map[string]string{ + status := map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), diff --git a/eventforwarder/forward_test.go b/eventforwarder/forward_test.go index cba9b89a2..0dcb848a6 100644 --- a/eventforwarder/forward_test.go +++ b/eventforwarder/forward_test.go @@ -31,7 +31,7 @@ var _ = Describe("Forward", func() { metadata, ).Return(int32(200), "success", nil) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -75,7 +75,7 @@ var _ = Describe("Forward", func() { metadata, ).Return(int32(200), "success", nil) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -120,7 +120,7 @@ var _ = Describe("Forward", func() { metadata, ).Return(int32(0), "", errors.New(errMsg)) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -230,7 +230,7 @@ game: game metadata, ) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -270,7 +270,7 @@ game: game metadata, ).Return(int32(0), "", errors.New(errMsg)) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -309,7 +309,7 @@ game: game metadata, ) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), @@ -347,7 +347,7 @@ game: game mockReporter.EXPECT().Report(reportersConstants.EventRPCDuration, gomock.Any()) - mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventRPCStatus, map[string]interface{}{ reportersConstants.TagGame: gameName, reportersConstants.TagScheduler: schedulerName, reportersConstants.TagHostname: Hostname(), diff --git a/migrations/migrations.go b/migrations/migrations.go index a0f80ec81..94d31d3c0 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -87,7 +87,7 @@ func migrations0001CreateschedulertableSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0001-CreateSchedulerTable.sql", size: 716, mode: os.FileMode(420), modTime: time.Unix(1533313922, 0)} + info := bindataFileInfo{name: "migrations/0001-CreateSchedulerTable.sql", size: 716, mode: os.FileMode(420), modTime: time.Unix(1529934313, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -107,7 +107,7 @@ func migrations0002CreateusertableSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0002-CreateUserTable.sql", size: 658, mode: os.FileMode(420), modTime: time.Unix(1533313922, 0)} + info := bindataFileInfo{name: "migrations/0002-CreateUserTable.sql", size: 658, mode: os.FileMode(420), modTime: time.Unix(1529934313, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -127,7 +127,7 @@ func migrations0003CreateschedulerversionstableSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0003-CreateSchedulerVersionsTable.sql", size: 664, mode: os.FileMode(420), modTime: time.Unix(1533313922, 0)} + info := bindataFileInfo{name: "migrations/0003-CreateSchedulerVersionsTable.sql", size: 664, mode: os.FileMode(420), modTime: time.Unix(1529934313, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -147,7 +147,7 @@ func migrations0004AlterschedulertableaddversionSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0004-AlterSchedulerTableAddVersion.sql", size: 243, mode: os.FileMode(420), modTime: time.Unix(1533313922, 0)} + info := bindataFileInfo{name: "migrations/0004-AlterSchedulerTableAddVersion.sql", size: 243, mode: os.FileMode(420), modTime: time.Unix(1529934313, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -167,7 +167,7 @@ func migrations0005AlterversiontypeSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0005-AlterVersionType.sql", size: 333, mode: os.FileMode(420), modTime: time.Unix(1533313922, 0)} + info := bindataFileInfo{name: "migrations/0005-AlterVersionType.sql", size: 333, mode: os.FileMode(420), modTime: time.Unix(1529934313, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/models/dogstatsd_metrics_reporter.go b/models/dogstatsd_metrics_reporter.go index dcb6882ef..ed417a92f 100644 --- a/models/dogstatsd_metrics_reporter.go +++ b/models/dogstatsd_metrics_reporter.go @@ -34,7 +34,7 @@ func (r *DogStatsdMetricsReporter) StartSegment(name string) map[string]interfac func (r *DogStatsdMetricsReporter) EndSegment(data map[string]interface{}, name string) { elapsedTime := time.Now().Sub(data["startTime"].(time.Time)).String() segment := data["segment"].(string) - tags := map[string]string{ + tags := map[string]interface{}{ reportersConstants.TagResponseTime: elapsedTime, reportersConstants.TagScheduler: r.Scheduler, reportersConstants.TagSegment: segment, @@ -66,7 +66,7 @@ func (r *DogStatsdMetricsReporter) EndDatastoreSegment(data map[string]interface datastore := data["datastore"].(string) operation := data["operation"].(string) table := data["table"].(string) - tags := map[string]string{ + tags := map[string]interface{}{ reportersConstants.TagResponseTime: elapsedTime, reportersConstants.TagScheduler: r.Scheduler, reportersConstants.TagSegment: strings.ToLower(fmt.Sprintf("%s/%s", datastore, operation)), @@ -91,7 +91,7 @@ func (r *DogStatsdMetricsReporter) StartExternalSegment(url string) map[string]i //EndExternalSegment stops segment func (r *DogStatsdMetricsReporter) EndExternalSegment(data map[string]interface{}) { elapsedTime := time.Now().Sub(data["startTime"].(time.Time)).String() - tags := map[string]string{ + tags := map[string]interface{}{ reportersConstants.TagResponseTime: elapsedTime, reportersConstants.TagScheduler: r.Scheduler, reportersConstants.TagSegment: data["segment"].(string), diff --git a/models/game_room_test.go b/models/game_room_test.go index 294d48650..fab19e16a 100644 --- a/models/game_room_test.go +++ b/models/game_room_test.go @@ -159,13 +159,13 @@ var _ = Describe("GameRoomManagement", func() { mockRedisClient.EXPECT().HGetAll(gomock.Any()).Return(goredis.NewStringStringMapResult(map[string]string{ "not": "empty", }, nil)).AnyTimes() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, "status": models.StatusCreating, "gauge": "2", }) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, }) @@ -264,13 +264,13 @@ var _ = Describe("GameRoomManagement", func() { mockRedisClient.EXPECT().HGetAll(gomock.Any()).Return(goredis.NewStringStringMapResult(map[string]string{ "not": "empty", }, nil)).AnyTimes() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, "status": models.StatusCreating, "gauge": "2", }) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, }) @@ -286,7 +286,7 @@ var _ = Describe("GameRoomManagement", func() { ) Expect(err).NotTo(HaveOccurred()) - mr.EXPECT().Report("gru.delete", map[string]string{ + mr.EXPECT().Report("gru.delete", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", reportersConstants.TagReason: "deletion_reason", @@ -327,13 +327,13 @@ var _ = Describe("GameRoomManagement", func() { mockRedisClient.EXPECT().HGetAll(gomock.Any()).Return(goredis.NewStringStringMapResult(map[string]string{ "not": "empty", }, nil)).AnyTimes() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, "status": models.StatusCreating, "gauge": "2", }) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, }) @@ -436,17 +436,17 @@ var _ = Describe("GameRoomManagement", func() { mockRedisClient.EXPECT().HGetAll(gomock.Any()).Return(goredis.NewStringStringMapResult(map[string]string{ "not": "empty", }, nil)).AnyTimes() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, "status": models.StatusCreating, "gauge": "2", }) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, }) - mr.EXPECT().Report("gru.delete", map[string]string{ + mr.EXPECT().Report("gru.delete", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", reportersConstants.TagReason: "failed_to_create_service_for_pod", @@ -496,13 +496,13 @@ var _ = Describe("GameRoomManagement", func() { mockRedisClient.EXPECT().HGetAll(gomock.Any()).Return(goredis.NewStringStringMapResult(map[string]string{ "not": "empty", }, nil)).AnyTimes() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, "status": models.StatusCreating, "gauge": "2", }) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: namespace, }) @@ -518,7 +518,7 @@ var _ = Describe("GameRoomManagement", func() { ) Expect(err).NotTo(HaveOccurred()) - mr.EXPECT().Report("gru.delete", map[string]string{ + mr.EXPECT().Report("gru.delete", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", reportersConstants.TagReason: "deletion_reason", diff --git a/models/namespace.go b/models/namespace.go index 1f0d27a4a..4aab40ed7 100644 --- a/models/namespace.go +++ b/models/namespace.go @@ -85,7 +85,7 @@ func (n *Namespace) DeletePods(clientset kubernetes.Interface, } for range pods.Items { - reporters.Report(reportersConstants.EventGruDelete, map[string]string{ + reporters.Report(reportersConstants.EventGruDelete, map[string]interface{}{ reportersConstants.TagGame: s.Game, reportersConstants.TagScheduler: s.Name, reportersConstants.TagReason: reportersConstants.ReasonNamespaceDeletion, diff --git a/models/pod.go b/models/pod.go index 9f8b41379..385319f13 100644 --- a/models/pod.go +++ b/models/pod.go @@ -168,7 +168,7 @@ func NewPod( err := pod.configureHostPorts(configYaml, clientset, redisClient) if err == nil { - reporters.Report(reportersConstants.EventGruNew, map[string]string{ + reporters.Report(reportersConstants.EventGruNew, map[string]interface{}{ reportersConstants.TagGame: configYaml.Game, reportersConstants.TagScheduler: configYaml.Name, }) @@ -194,7 +194,7 @@ func NewPodWithContainers( } err := pod.configureHostPorts(configYaml, clientset, redisClient) if err == nil { - reporters.Report(reportersConstants.EventGruNew, map[string]string{ + reporters.Report(reportersConstants.EventGruNew, map[string]interface{}{ reportersConstants.TagGame: configYaml.Game, reportersConstants.TagScheduler: configYaml.Name, }) @@ -257,7 +257,7 @@ func (p *Pod) Delete(clientset kubernetes.Interface, } if err == nil { - reporters.Report(reportersConstants.EventGruDelete, map[string]string{ + reporters.Report(reportersConstants.EventGruDelete, map[string]interface{}{ reportersConstants.TagGame: p.Game, reportersConstants.TagScheduler: p.Namespace, reportersConstants.TagReason: reason, diff --git a/models/pod_test.go b/models/pod_test.go index ba6bfd616..821142fb9 100644 --- a/models/pod_test.go +++ b/models/pod_test.go @@ -42,7 +42,7 @@ var _ = Describe("Pod", func() { ) createPod := func() (*models.Pod, error) { - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) @@ -142,7 +142,7 @@ var _ = Describe("Pod", func() { _, err = pod.Create(mockClientset) Expect(err).NotTo(HaveOccurred()) - mr.EXPECT().Report("gru.delete", map[string]string{ + mr.EXPECT().Report("gru.delete", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", reportersConstants.TagReason: "deletion_reason", @@ -164,7 +164,7 @@ var _ = Describe("Pod", func() { It("should create pod with two containers", func() { mockPortChooser.EXPECT().Choose(portStart, portEnd, 1).Return([]int{5001}) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) @@ -252,14 +252,14 @@ var _ = Describe("Pod", func() { }) It("should return correct ports if pod already exists", func() { - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) mockPortChooser.EXPECT().Choose(portStart, portEnd, 1).Return([]int{5001}) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) @@ -433,7 +433,7 @@ var _ = Describe("Pod", func() { }) It("should create pod without requests and limits", func() { - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) @@ -555,7 +555,7 @@ var _ = Describe("Pod", func() { _, err = pod.Create(mockClientset) Expect(err).NotTo(HaveOccurred()) - mr.EXPECT().Report("gru.delete", map[string]string{ + mr.EXPECT().Report("gru.delete", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", reportersConstants.TagReason: "deletion_reason", diff --git a/models/room.go b/models/room.go index 60c832a51..cddbf8161 100644 --- a/models/room.go +++ b/models/room.go @@ -224,14 +224,14 @@ func (r *Room) reportStatus( } func reportPing(game, scheduler string) error { - return reporters.Report(reportersConstants.EventGruPing, map[string]string{ + return reporters.Report(reportersConstants.EventGruPing, map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: scheduler, }) } func reportStatus(game, scheduler, status, gauge string) error { - return reporters.Report(reportersConstants.EventGruStatus, map[string]string{ + return reporters.Report(reportersConstants.EventGruStatus, map[string]interface{}{ reportersConstants.TagGame: game, reportersConstants.TagScheduler: scheduler, "status": status, diff --git a/models/room_address_test.go b/models/room_address_test.go index 52cfad4ce..8a5af1c7c 100644 --- a/models/room_address_test.go +++ b/models/room_address_test.go @@ -106,7 +106,7 @@ var _ = Describe("AddressGetter", func() { Return(goredis.NewStringResult(portRange, nil)) mockPortChooser.EXPECT().Choose(portStart, portEnd, 2).Return([]int{5000, 5001}) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) @@ -206,7 +206,7 @@ var _ = Describe("AddressGetter", func() { Return(goredis.NewStringResult(portRange, nil)) mockPortChooser.EXPECT().Choose(portStart, portEnd, 2).Return([]int{5000, 5001}) - mr.EXPECT().Report("gru.new", map[string]string{ + mr.EXPECT().Report("gru.new", map[string]interface{}{ reportersConstants.TagGame: "pong", reportersConstants.TagScheduler: "pong-free-for-all", }) diff --git a/models/room_test.go b/models/room_test.go index 9c730229a..81b038ae5 100644 --- a/models/room_test.go +++ b/models/room_test.go @@ -42,7 +42,7 @@ var _ = Describe("Room", func() { mockPipeline.EXPECT().SCard(statusKey).Return(redis.NewIntResult(int64(5), nil)) mockPipeline.EXPECT().Exec() - mr.EXPECT().Report("gru.status", map[string]string{ + mr.EXPECT().Report("gru.status", map[string]interface{}{ reportersConstants.TagGame: "game-name", reportersConstants.TagScheduler: scheduler, "status": status, diff --git a/reporters/constants/constants.go b/reporters/constants/constants.go index c0b2ab1b7..97b5ad4be 100644 --- a/reporters/constants/constants.go +++ b/reporters/constants/constants.go @@ -7,6 +7,8 @@ const ( EventGruDelete = "gru.delete" EventGruStatus = "gru.status" + EventSchedulerUpdate = "scheduler.update" + EventRPCStatus = "rpc.status" EventRPCDuration = "rpc.duration" diff --git a/reporters/dogstatsd.go b/reporters/dogstatsd.go index 8d64d3560..ee64c59eb 100644 --- a/reporters/dogstatsd.go +++ b/reporters/dogstatsd.go @@ -24,23 +24,29 @@ type DogStatsD struct { region string } +func toMapStringString(o map[string]interface{}) map[string]string { + n := map[string]string{} + for k, v := range o { + if str, ok := v.(string); ok { + n[k] = str + } + } + return n +} + // Report finds a matching handler to some 'event' metric and delegates // further actions to it -func (d *DogStatsD) Report(event string, opts map[string]string) error { +func (d *DogStatsD) Report(event string, opts map[string]interface{}) error { handlerI, prs := handlers.Find(event) - if prs == false { return fmt.Errorf("reportHandler for %s doesn't exist", event) } - handler := handlerI.(func(dogstatsd.Client, string, map[string]string) error) - opts[constants.TagRegion] = d.region - - err := handler(d.client, event, opts) + handler := handlerI.(func(dogstatsd.Client, string, map[string]string) error) + err := handler(d.client, event, toMapStringString(opts)) if err != nil { d.logger.Error(err) } - return err } @@ -53,7 +59,7 @@ func MakeDogStatsD(config *viper.Viper, logger *logrus.Logger, r *Reporters) { } } -func loadDefaultConfigs(c *viper.Viper) { +func loadDefaultDogStatsDConfigs(c *viper.Viper) { c.SetDefault("reporters.dogstatsd.host", "localhost:8125") c.SetDefault("reporters.dogstatsd.prefix", "test.") c.SetDefault("reporters.dogstatsd.region", "test") @@ -61,7 +67,7 @@ func loadDefaultConfigs(c *viper.Viper) { // NewDogStatsD creates a DogStatsD struct using host and prefix from config func NewDogStatsD(config *viper.Viper, logger *logrus.Logger) (*DogStatsD, error) { - loadDefaultConfigs(config) + loadDefaultDogStatsDConfigs(config) host := config.GetString("reporters.dogstatsd.host") prefix := config.GetString("reporters.dogstatsd.prefix") c, err := dogstatsd.New(host, prefix) diff --git a/reporters/dogstatsd/handlers.go b/reporters/dogstatsd/handlers.go index 7a267ac02..ba8bcd9b3 100644 --- a/reporters/dogstatsd/handlers.go +++ b/reporters/dogstatsd/handlers.go @@ -42,6 +42,7 @@ func createTags(opts map[string]string) []string { return tags } +// func createAllowedTags(opts map[string]string, allowed []string) []string { func createAllowedTags(opts map[string]string, allowed []string) []string { var tags []string for _, tag := range allowed { diff --git a/reporters/dogstatsd_test.go b/reporters/dogstatsd_test.go index 6c532091d..71e445d05 100644 --- a/reporters/dogstatsd_test.go +++ b/reporters/dogstatsd_test.go @@ -24,6 +24,14 @@ var _ = Describe("DogStatsD", func() { opts map[string]string ) + toMapStringInterface := func(o map[string]string) map[string]interface{} { + n := map[string]interface{}{} + for k, v := range o { + n[k] = v + } + return n + } + BeforeEach(func() { r = reporters.NewReporters() c = mocks.NewMockClient(mockCtrl) @@ -41,7 +49,6 @@ var _ = Describe("DogStatsD", func() { It("GruIncrHandler should Incr event metric by 1", func() { c.EXPECT().Incr("gru.new", []string{"game:pong"}, float64(1)) - handlers.GruIncrHandler(c, "gru.new", opts) }) @@ -56,9 +63,8 @@ var _ = Describe("DogStatsD", func() { It("Report(gru.new, opts) should Incr gru.new", func() { c.EXPECT().Incr("gru.new", gomock.Any(), float64(1)) - d := reporters.NewDogStatsDFromClient(c, "test") - err := d.Report("gru.new", opts) + err := d.Report("gru.new", toMapStringInterface(opts)) Expect(err).NotTo(HaveOccurred()) }) @@ -69,7 +75,7 @@ var _ = Describe("DogStatsD", func() { d := reporters.NewDogStatsDFromClient(c, "test") opts["status"] = "creating" opts["gauge"] = "5" - err := d.Report("gru.status", opts) + err := d.Report("gru.status", toMapStringInterface(opts)) Expect(err).NotTo(HaveOccurred()) }) }) diff --git a/reporters/http.go b/reporters/http.go new file mode 100644 index 000000000..112a465d1 --- /dev/null +++ b/reporters/http.go @@ -0,0 +1,115 @@ +// maestro +// https://github.com/topfreegames/maestro +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license +// Copyright © 2018 Top Free Games + +package reporters + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + handlers "github.com/topfreegames/maestro/reporters/http" +) + +// HTTP reports metrics to an http endpoint +type HTTP struct { + client *HTTPClient + region string + logger *logrus.Logger +} + +// HTTPClient implements reporters::http.Client interface +type HTTPClient struct { + client *http.Client + putURL string +} + +// NewHTTPClient ctor +func NewHTTPClient(putURL string) *HTTPClient { + return &HTTPClient{ + client: &http.Client{}, + putURL: putURL, + } +} + +// Send makes a PUT HTTP call to the underlying client +func (c *HTTPClient) Send(opts map[string]interface{}) error { + bodyMap := map[string]interface{}{} + bodyMap["tags"] = opts["tags"] + delete(opts, "tags") + bodyMap["metadata"] = opts + b, err := json.Marshal(bodyMap) + if err != nil { + return err + } + req, err := http.NewRequest(http.MethodPut, c.putURL, strings.NewReader(string(b))) + if err != nil { + return err + } + res, err := c.client.Do(req) + if err != nil { + return err + } + if res.StatusCode > 299 { + return fmt.Errorf("reporters.HTTP error status code: %d", res.StatusCode) + } + return nil +} + +// Report finds a matching handler to some 'event' metric and delegates +// further actions to it +// HTTP reports have the following format in json: +// { "tags": [...], "metadata": {...} } +// tags = opts[tags] +// metadata = opts - opts[tags] +func (h *HTTP) Report(event string, opts map[string]interface{}) error { + handlerI, prs := handlers.Find(event) + if prs == false { + return fmt.Errorf("reportHandler for %s doesn't exist", event) + } + opts["tags"] = []string{"maestro", event, h.region} + if opts["error"] != nil { + if err, ok := opts["error"].(error); ok { + opts["error"] = err.Error() + } + } + opts["region"] = h.region + handler := + handlerI.(func(handlers.Client, map[string]interface{}) error) + err := handler(h.client, opts) + if err != nil { + h.logger.Error(err) + } + return err +} + +// MakeHTTP adds an HTTP struct to the Reporters' singleton +func MakeHTTP(config *viper.Viper, logger *logrus.Logger, r *Reporters) { + httpR, err := NewHTTP(config, logger) + + if err == nil { + r.SetReporter("http", httpR) + } +} + +func loadDefaultHTTPConfigs(c *viper.Viper) { + c.SetDefault("reporters.http.putURL", "http://localhost:8080") + c.SetDefault("reporters.http.region", "test") +} + +// NewHTTP creates an HTTP struct using putURL and region from config +func NewHTTP(config *viper.Viper, logger *logrus.Logger) (*HTTP, error) { + loadDefaultHTTPConfigs(config) + putURL := config.GetString("reporters.http.putURL") + region := config.GetString("reporters.http.region") + client := NewHTTPClient(putURL) + httpR := &HTTP{client: client, region: region} + return httpR, nil +} diff --git a/reporters/http/client.go b/reporters/http/client.go new file mode 100644 index 000000000..a8d3a46eb --- /dev/null +++ b/reporters/http/client.go @@ -0,0 +1,6 @@ +package http + +// Client is a contract that http reporters clients must implement +type Client interface { + Send(opts map[string]interface{}) error +} diff --git a/reporters/http/handlers.go b/reporters/http/handlers.go new file mode 100644 index 000000000..78c99662a --- /dev/null +++ b/reporters/http/handlers.go @@ -0,0 +1,27 @@ +// maestro +// https://github.com/topfreegames/maestro +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license +// Copyright © 2018 Top Free Games + +package http + +import ( + "github.com/topfreegames/maestro/reporters/constants" +) + +var handlers = map[string]interface{}{ + constants.EventSchedulerUpdate: AnyHandler, +} + +// Find looks for a matching handler to a given event +func Find(event string) (interface{}, bool) { + handlerI, prs := handlers[event] + return handlerI, prs +} + +// AnyHandler sends the respective event to an HTTP endpoint +func AnyHandler(client Client, opts map[string]interface{}) error { + return client.Send(opts) +} diff --git a/reporters/mocks/reporter.go b/reporters/mocks/reporter.go index c1c0fa0ec..6700130fe 100644 --- a/reporters/mocks/reporter.go +++ b/reporters/mocks/reporter.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: reporters/reporter.go +// Source: reporter.go -// Package mock_reporters is a generated GoMock package. +// Package mocks is a generated GoMock package. package mocks import ( @@ -33,7 +33,7 @@ func (m *MockReporter) EXPECT() *MockReporterMockRecorder { } // Report mocks base method -func (m *MockReporter) Report(event string, opts map[string]string) error { +func (m *MockReporter) Report(event string, opts map[string]interface{}) error { ret := m.ctrl.Call(m, "Report", event, opts) ret0, _ := ret[0].(error) return ret0 diff --git a/reporters/reporter.go b/reporters/reporter.go index fc0b736f3..70c08e947 100644 --- a/reporters/reporter.go +++ b/reporters/reporter.go @@ -17,7 +17,7 @@ import ( // Reporter implementations are responsible for reporting // events to any sink that wants to consume them type Reporter interface { - Report(event string, opts map[string]string) error + Report(event string, opts map[string]interface{}) error } // Reporters hold a map of structs that implement the Reporter interface @@ -42,7 +42,7 @@ func (r *Reporters) GetReporter(key string) (Reporter, bool) { } // Report is Reporters' implementation of the Reporter interface -func (r *Reporters) Report(event string, opts map[string]string) error { +func (r *Reporters) Report(event string, opts map[string]interface{}) error { for _, reporter := range r.reporters { reporter.Report(event, opts) } @@ -55,7 +55,7 @@ func HasReporters() bool { } // Report calls Report() in Reporters' singleton -func Report(event string, opts map[string]string) error { +func Report(event string, opts map[string]interface{}) error { return GetInstance().Report(event, opts) } @@ -64,6 +64,9 @@ func MakeReporters(config *viper.Viper, logger *logrus.Logger) { if config.IsSet("reporters.dogstatsd") { MakeDogStatsD(config, logger, GetInstance()) } + if config.IsSet("reporters.http") { + MakeHTTP(config, logger, GetInstance()) + } } // NewReporters ctor diff --git a/reporters/reporter_test.go b/reporters/reporter_test.go index 657ddac98..ff8a2ed5b 100644 --- a/reporters/reporter_test.go +++ b/reporters/reporter_test.go @@ -17,7 +17,7 @@ import ( var _ = Describe("Reporters", func() { It("Reporters.Report() must call Report on all children", func() { - opts := map[string]string{"game": "pong"} + opts := map[string]interface{}{"game": "pong"} for _, mr := range mrs { mr.EXPECT().Report(EventGruNew, opts) diff --git a/watcher/watcher.go b/watcher/watcher.go index b19d6eb50..32cc2c3b3 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -290,7 +290,7 @@ func (w *Watcher) ReportRoomsStatuses() error { } for _, r := range roomDataSlice { - reporters.Report(reportersConstants.EventGruStatus, map[string]string{ + reporters.Report(reportersConstants.EventGruStatus, map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": r.Status, @@ -969,7 +969,7 @@ func (w *Watcher) PodStatesCount() { for reason, count := range restartCount { logger.Debugf("sending result to statsd: {%s:%d}", reason, count) - reporters.Report(reportersConstants.EventPodLastStatus, map[string]string{ + reporters.Report(reportersConstants.EventPodLastStatus, map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, reportersConstants.TagReason: reason, diff --git a/watcher/watcher_test.go b/watcher/watcher_test.go index de9d8b105..b65aa3781 100644 --- a/watcher/watcher_test.go +++ b/watcher/watcher_test.go @@ -616,7 +616,7 @@ var _ = Describe("Watcher", func() { mockReporter.EXPECT().Report( reportersConstants.EventGruStatus, - map[string]string{ + map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": models.StatusCreating, @@ -625,7 +625,7 @@ var _ = Describe("Watcher", func() { ) mockReporter.EXPECT().Report( reportersConstants.EventGruStatus, - map[string]string{ + map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": models.StatusReady, @@ -634,7 +634,7 @@ var _ = Describe("Watcher", func() { ) mockReporter.EXPECT().Report( reportersConstants.EventGruStatus, - map[string]string{ + map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": models.StatusOccupied, @@ -643,7 +643,7 @@ var _ = Describe("Watcher", func() { ) mockReporter.EXPECT().Report( reportersConstants.EventGruStatus, - map[string]string{ + map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": models.StatusTerminating, @@ -652,7 +652,7 @@ var _ = Describe("Watcher", func() { ) mockReporter.EXPECT().Report( reportersConstants.EventGruStatus, - map[string]string{ + map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, "status": models.StatusReadyOrOccupied, @@ -4238,7 +4238,7 @@ var _ = Describe("Watcher", func() { Expect(err).ToNot(HaveOccurred()) } - mockReporter.EXPECT().Report(reportersConstants.EventPodLastStatus, map[string]string{ + mockReporter.EXPECT().Report(reportersConstants.EventPodLastStatus, map[string]interface{}{ reportersConstants.TagGame: w.GameName, reportersConstants.TagScheduler: w.SchedulerName, reportersConstants.TagReason: reason,