mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	Implemented health checker for textures provider from Mojang's API
This commit is contained in:
		| @@ -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. | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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())) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user