From 4c21fc5c9045f2260768c6028ddb223f537c32df Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Thu, 30 Apr 2020 23:16:22 +0300 Subject: [PATCH] Implemented health checker for textures provider from Mojang's API --- CHANGELOG.md | 1 + di/mojang_textures.go | 15 +++++ eventsubscribers/health_checkers.go | 67 +++++++++++++------ eventsubscribers/health_checkers_test.go | 39 ++++++++++- mojangtextures/batch_uuids_provider_test.go | 9 ++- .../mojang_api_textures_provider.go | 2 +- .../mojang_api_textures_provider_test.go | 2 + 7 files changed, 111 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1adda7..4d78627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New configuration param `QUEUE_STRATEGY` with the default value `periodic`. - New configuration params: `MOJANG_API_BASE_URL` and `MOJANG_SESSION_SERVER_BASE_URL`, that allow you to spoof Mojang API base addresses. +- New health checker, that ensures that response for textures provider from Mojang's API is valid. ### Fixed - Handle the case when there is no textures property in Mojang's response. diff --git a/di/mojang_textures.go b/di/mojang_textures.go index 5620a6b..e63716b 100644 --- a/di/mojang_textures.go +++ b/di/mojang_textures.go @@ -189,6 +189,7 @@ func newMojangTexturesBatchUUIDsProviderFullBusStrategy(config *viper.Viper) *mo } func newMojangTexturesRemoteUUIDsProvider( + container *di.Container, config *viper.Viper, emitter mojangtextures.Emitter, ) (*mojangtextures.RemoteApiUuidsProvider, error) { @@ -197,6 +198,20 @@ func newMojangTexturesRemoteUUIDsProvider( return nil, fmt.Errorf("unable to parse remote url: %w", err) } + if err := container.Provide(func(emitter es.Subscriber, config *viper.Viper) *namedHealthChecker { + config.SetDefault("healthcheck.mojang_api_textures_provider_cool_down_duration", time.Minute+10*time.Second) + + return &namedHealthChecker{ + Name: "mojang-api-textures-provider-response-checker", + Checker: es.MojangApiTexturesProviderResponseChecker( + emitter, + config.GetDuration("healthcheck.mojang_api_textures_provider_cool_down_duration"), + ), + } + }); err != nil { + return nil, err + } + return &mojangtextures.RemoteApiUuidsProvider{ Emitter: emitter, Url: *remoteUrl, diff --git a/eventsubscribers/health_checkers.go b/eventsubscribers/health_checkers.go index 2eb4788..48d6f09 100644 --- a/eventsubscribers/health_checkers.go +++ b/eventsubscribers/health_checkers.go @@ -32,33 +32,16 @@ func DatabaseChecker(connection Pingable) healthcheck.CheckerFunc { } func MojangBatchUuidsProviderResponseChecker(dispatcher Subscriber, resetDuration time.Duration) healthcheck.CheckerFunc { - var mutex sync.Mutex - var lastCallErr error - var expireTimer *time.Timer + errHolder := &expiringErrHolder{D: resetDuration} dispatcher.Subscribe( "mojang_textures:batch_uuids_provider:result", func(usernames []string, profiles []*mojang.ProfileInfo, err error) { - mutex.Lock() - defer mutex.Unlock() - - lastCallErr = err - if expireTimer != nil { - expireTimer.Stop() - } - - expireTimer = time.AfterFunc(resetDuration, func() { - mutex.Lock() - lastCallErr = nil - mutex.Unlock() - }) + errHolder.Set(err) }, ) return func(ctx context.Context) error { - mutex.Lock() - defer mutex.Unlock() - - return lastCallErr + return errHolder.Get() } } @@ -82,3 +65,47 @@ func MojangBatchUuidsProviderQueueLengthChecker(dispatcher Subscriber, maxLength return errors.New("the maximum number of tasks in the queue has been exceeded") } } + +func MojangApiTexturesProviderResponseChecker(dispatcher Subscriber, resetDuration time.Duration) healthcheck.CheckerFunc { + errHolder := &expiringErrHolder{D: resetDuration} + dispatcher.Subscribe( + "mojang_textures:mojang_api_textures_provider:after_request", + func(uuid string, profile *mojang.SignedTexturesResponse, err error) { + errHolder.Set(err) + }, + ) + + return func(ctx context.Context) error { + return errHolder.Get() + } +} + +type expiringErrHolder struct { + D time.Duration + err error + l sync.Mutex + t *time.Timer +} + +func (h *expiringErrHolder) Get() error { + h.l.Lock() + defer h.l.Unlock() + + return h.err +} + +func (h *expiringErrHolder) Set(err error) { + h.l.Lock() + defer h.l.Unlock() + if h.t != nil { + h.t.Stop() + h.t = nil + } + + h.err = err + if err != nil { + h.t = time.AfterFunc(h.D, func() { + h.Set(nil) + }) + } +} diff --git a/eventsubscribers/health_checkers_test.go b/eventsubscribers/health_checkers_test.go index 5ca269c..a3e9187 100644 --- a/eventsubscribers/health_checkers_test.go +++ b/eventsubscribers/health_checkers_test.go @@ -56,7 +56,7 @@ func TestMojangBatchUuidsProviderChecker(t *testing.T) { checker := MojangBatchUuidsProviderResponseChecker(d, time.Millisecond) assert.Nil(t, checker(context.Background())) }) - // + t.Run("when no error occurred", func(t *testing.T) { d := dispatcher.New() checker := MojangBatchUuidsProviderResponseChecker(d, time.Millisecond) @@ -107,3 +107,40 @@ func TestMojangBatchUuidsProviderQueueLengthChecker(t *testing.T) { } }) } + +func TestMojangApiTexturesProviderResponseChecker(t *testing.T) { + t.Run("empty state", func(t *testing.T) { + d := dispatcher.New() + checker := MojangApiTexturesProviderResponseChecker(d, time.Millisecond) + assert.Nil(t, checker(context.Background())) + }) + + t.Run("when no error occurred", func(t *testing.T) { + d := dispatcher.New() + checker := MojangApiTexturesProviderResponseChecker(d, time.Millisecond) + d.Emit("mojang_textures:mojang_api_textures_provider:after_request", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + &mojang.SignedTexturesResponse{}, + nil, + ) + assert.Nil(t, checker(context.Background())) + }) + + t.Run("when error occurred", func(t *testing.T) { + d := dispatcher.New() + checker := MojangApiTexturesProviderResponseChecker(d, time.Millisecond) + err := errors.New("some error occurred") + d.Emit("mojang_textures:mojang_api_textures_provider:after_request", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil, err) + assert.Equal(t, err, checker(context.Background())) + }) + + t.Run("should reset value after passed duration", func(t *testing.T) { + d := dispatcher.New() + checker := MojangApiTexturesProviderResponseChecker(d, 20*time.Millisecond) + err := errors.New("some error occurred") + d.Emit("mojang_textures:mojang_api_textures_provider:after_request", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil, err) + assert.Equal(t, err, checker(context.Background())) + time.Sleep(40 * time.Millisecond) + assert.Nil(t, checker(context.Background())) + }) +} diff --git a/mojangtextures/batch_uuids_provider_test.go b/mojangtextures/batch_uuids_provider_test.go index 711a03c..c237df6 100644 --- a/mojangtextures/batch_uuids_provider_test.go +++ b/mojangtextures/batch_uuids_provider_test.go @@ -196,10 +196,15 @@ func (suite *batchUuidsProviderTestSuite) TestShouldNotSendRequestWhenNoJobsAreR close(done) }) - _ = suite.GetUuidAsync("username") // Schedule one username to run the queue + r := suite.GetUuidAsync("username") // Schedule one username to run the queue suite.Strategy.Iterate(0, 1) // Return no jobs and indicate that there is one job in queue - <-done + select { + case <-r: + // fail + case <-done: + return + } } // Test written for multiple usernames to ensure that the error diff --git a/mojangtextures/mojang_api_textures_provider.go b/mojangtextures/mojang_api_textures_provider.go index 543ffda..f23c9d1 100644 --- a/mojangtextures/mojang_api_textures_provider.go +++ b/mojangtextures/mojang_api_textures_provider.go @@ -13,7 +13,7 @@ type MojangApiTexturesProvider struct { func (ctx *MojangApiTexturesProvider) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) { ctx.Emit("mojang_textures:mojang_api_textures_provider:before_request", uuid) result, err := uuidToTextures(uuid, true) - ctx.Emit("mojang_textures:mojang_api_textures_provider:after_request", result, err) + ctx.Emit("mojang_textures:mojang_api_textures_provider:after_request", uuid, result, err) return result, err } diff --git a/mojangtextures/mojang_api_textures_provider_test.go b/mojangtextures/mojang_api_textures_provider_test.go index e837a48..8b6ada9 100644 --- a/mojangtextures/mojang_api_textures_provider_test.go +++ b/mojangtextures/mojang_api_textures_provider_test.go @@ -64,6 +64,7 @@ func (suite *mojangApiTexturesProviderTestSuite) TestGetTextures() { ).Once() suite.Emitter.On("Emit", "mojang_textures:mojang_api_textures_provider:after_request", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", expectedResult, nil, ).Once() @@ -85,6 +86,7 @@ func (suite *mojangApiTexturesProviderTestSuite) TestGetTexturesWithError() { ).Once() suite.Emitter.On("Emit", "mojang_textures:mojang_api_textures_provider:after_request", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", expectedResponse, expectedError, ).Once()